一、客户端认证
1、JWT Bearer
|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 关于 JWT Bearer 客户端认证的进一步详情,请参考OAuth 2.0客户端认证和授权许可的 JSON Web Token (JWT)简介。 |
JWT Bearer 客户端认证的默认实现是 NimbusJwtClientAuthenticationParametersConverter
,它是一个 Converter
,通过在 client_assertion
参数中添加签名的JSON Web Token(JWS)来定制令牌请求参数。
用于签署 JWS 的 java.security.PrivateKey
或 javax.crypto.SecretKey
由与 NimbusJwtClientAuthenticationParametersConverter
相关的 com.nimbusds.jose.jwk.JWK
解析器提供。
使用private_key_jwt进行认证
给出以下Spring Boot 2.x属性,用于OAuth 2.0客户端注册。
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-authentication-method: private_key_jwt
authorization-grant-type: authorization_code
...
下面的例子显示了如何配置 DefaultAuthorizationCodeTokenResponseClient
。
-
Java
Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
// Assuming RSA key type
RSAPublicKey publicKey = ...
RSAPrivateKey privateKey = ...
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
return null;
};OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(
new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));DefaultAuthorizationCodeTokenResponseClient tokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);
使用 client_secret_jwt 进行认证
给出以下Spring Boot 2.x属性,用于OAuth 2.0客户端注册。
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-secret: okta-client-secret
client-authentication-method: client_secret_jwt
authorization-grant-type: client_credentials
...
下面的例子显示了如何配置 DefaultClientCredentialsTokenResponseClient
。
-
Java
Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {
SecretKeySpec secretKey = new SecretKeySpec(
clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),
"HmacSHA256");
return new OctetSequenceKey.Builder(secretKey)
.keyID(UUID.randomUUID().toString())
.build();
}
return null;
};OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(
new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));DefaultClientCredentialsTokenResponseClient tokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);
自定义JWT断言(JWT assertion)
NimbusJwtClientAuthenticationParametersConverter
产生的JWT默认包含 iss
、sub
、aud
、jti
、iat
和 exp
等 claim。你可以通过提供一个 Consumer<NimbusJwtClientAuthenticationParametersConverter.JwtClientAuthenticationContext<T>>
给 `setJwtClientAssertionCustomizer()`来定制头信息和/或claim。下面的例子显示了如何定制JWT的 claim。
-
Java
Function<ClientRegistration, JWK> jwkResolver = ...
NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> converter =
new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver);
converter.setJwtClientAssertionCustomizer((context) -> {
context.getHeaders().header("custom-header", "header-value");
context.getClaims().claim("custom-claim", "claim-value");
});
二、 OAuth2 客户端授权
1、解析授权客户端
@RegisteredOAuth2AuthorizedClient
注解提供了将方法参数解析为 OAuth2AuthorizedClient
类型的参数值的能力。与使用 OAuth2AuthorizedClientManager
或 OAuth2AuthorizedClientService
来访问 OAuth2AuthorizedClient
相比,这是一个方便的选择。下面的例子展示了如何使用 @RegisteredOAuth2AuthorizedClient
。
-
Java
@Controller
public class OAuth2ClientController {@GetMapping("/") public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) { OAuth2AccessToken accessToken = authorizedClient.getAccessToken(); ... return "index"; }
}
@RegisteredOAuth2AuthorizedClient
注解由 OAuth2AuthorizedClientArgumentResolver
处理,它直接使用 OAuth2AuthorizedClientManager ,因此继承其能力。
2、为Servlet环境整合WebClient
OAuth 2.0客户端支持通过使用 ExchangeFilterFunction
与 WebClient
整合。
ServletOAuth2AuthorizedClientExchangeFilterFunction
提供了一种机制,通过使用 OAuth2AuthorizedClient
并包括相关的 OAuth2AccessToken
作为承载令牌来请求受保护的资源。它直接使用 OAuth2AuthorizedClientManager ,因此,它继承了以下功能。
-
如果客户还没有得到授权,就会请求一个
OAuth2AccessToken
。-
authorization_code
: 触发Authorization
请求重定向,启动流程。 -
client_credentials
: 访问令牌是直接从令牌端点获得的。 -
password
: 访问令牌是直接从令牌端点获得的。
-
-
如果
OAuth2AccessToken
已经过期,如果有OAuth2AuthorizedClientProvider
可以执行授权,它将被刷新(或更新)。
下面的代码显示了一个如何配置支持OAuth 2.0客户端的 WebClient
的例子。
-
Java
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
提供授权客户端
ServletOAuth2AuthorizedClientExchangeFilterFunction
通过从 ClientRequest.attributes()
(请求属性)中解析 OAuth2AuthorizedClient
,来确定(对一个请求)要使用的客户端。
下面的代码显示了如何设置一个 OAuth2AuthorizedClient
作为请求属性。
-
Java
@GetMapping("/")
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
String resourceUri = ...String body = webClient .get() .uri(resourceUri) .attributes(oauth2AuthorizedClient(authorizedClient)) .retrieve() .bodyToMono(String.class) .block(); ... return "index";
}
|-----------------------------------------------------------------------------------------------------------|
| oauth2AuthorizedClient()
is a static
method in ServletOAuth2AuthorizedClientExchangeFilterFunction
. |
下面的代码显示了如何设置 ClientRegistration.getRegistrationId()
作为请求属性。
-
Java
@GetMapping("/")
public String index() {
String resourceUri = ...String body = webClient .get() .uri(resourceUri) .attributes(clientRegistrationId("okta")) .retrieve() .bodyToMono(String.class) .block(); ... return "index";
}
|---------------------------------------------------------------------------------------------------------|
| clientRegistrationId()
is a static
method in ServletOAuth2AuthorizedClientExchangeFilterFunction
. |
默认授权客户端
如果 OAuth2AuthorizedClient
或 ClientRegistration.getRegistrationId()
都没有作为请求属性(request attribute)提供,ServletOAuth2AuthorizedClientExchangeFilterFunction
可以根据其配置决定使用默认客户端。
如果配置了 setDefaultOAuth2AuthorizedClient(true)
,并且用户已经通过 HttpSecurity.oauth2Login()
进行了认证,则使用与当前 OAuth2AuthenticationToken
关联的 OAuth2AccessToken
。
下面的代码显示了具体的配置。
-
Java
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
|-------------------------------|
| 对这个功能要谨慎,因为所有的HTTP请求都会收到访问令牌。 |
另外,如果 setDefaultClientRegistrationId("okta")
配置了一个有效的 ClientRegistration
,则会使用与 OAuth2AuthorizedClient
相关的 OAuth2AccessToken
。
下面的代码显示了具体的配置。
-
Java
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("okta");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
|-------------------------------|
| 对这个功能要谨慎,因为所有的HTTP请求都会收到访问令牌。 |