在上一篇中我们分析了 OAuth2LoginAuthentiationWebFilter 是如何拦截请求。拦截到的请求经过 ServerAuthenticationConverter 转换成了 Authentication 认证信息对象。那它如何把请求转换成 Authentication 认证对象的呢?
初始化ServerAuthenticationConverter
在 ServerHttpSecurity 类的内部类 OAuth2LoginSpec 的 configure() 方法内,OAuth2LoginAuthenticationWebFilter 初始化了 ServerAuthenticationConverter。如果我们没有指定 ServerAuthenticationConverter,就创建默认的;否则使用指定的。大概创建流程如下源码所示。
protected void configure(ServerHttpSecurity http) {
// 省略其他配置
AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager, authorizedClientRepository);
authenticationFilter.setServerAuthenticationConverter(this.getAuthenticationConverter(clientRegistrationRepository));
}
private ServerAuthenticationConverter getAuthenticationConverter(ReactiveClientRegistrationRepository clientRegistrationRepository) {
if (this.authenticationConverter != null) {
return this.authenticationConverter;
} else {
ServerOAuth2AuthorizationCodeAuthenticationTokenConverter delegate = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository);
delegate.setAuthorizationRequestRepository(this.getAuthorizationRequestRepository());
ServerAuthenticationConverter authenticationConverter = (exchange) -> {
return delegate.convert(exchange).onErrorMap(OAuth2AuthorizationException.class, (e) -> {
return new OAuth2AuthenticationException(e.getError(), e.getError().toString());
});
};
this.authenticationConverter = authenticationConverter;
return authenticationConverter;
}
}
ServerAuthenticationConvert接口提供 covert 的方法。我们可以从上述源码观察到,默认是由 ServerOAuth2AuthorizationCodeAuthenticationTokenConverter 来实现把请求转换成 Authentication 认证信息对象。
ServerAuthenticationConverter默认实现类
首先从缓存中获取并移除 OAuth2AuthorizationRequest 对象,如果获取不到 OAuth2AuthorizationRequest 对象,则报错;然后从 OAuth2AuthorizationRequest 的属性中获取 registration_id ,然后再根据 registration_id 查找 ClientRegistration 对象,最后创建了OAuth2AuthorizationCodeAuthenticationToken 的对象实例,它包含了:clientRegistration 对象、OAuth2AuthorizationRequest 对象、OAuth2AuthorizationResponse 对象。源码如下所示:
public Mono<Authentication> convert(ServerWebExchange serverWebExchange) {
return this.authorizationRequestRepository.removeAuthorizationRequest(serverWebExchange).switchIfEmpty(this.oauth2AuthorizationException("authorization_request_not_found")).flatMap((authorizationRequest) -> {
return this.authenticationRequest(serverWebExchange, authorizationRequest);
});
}
private Mono<OAuth2AuthorizationCodeAuthenticationToken> authenticationRequest(ServerWebExchange exchange, OAuth2AuthorizationRequest authorizationRequest) {
return Mono.just(authorizationRequest).map(OAuth2AuthorizationRequest::getAttributes).flatMap((attributes) -> {
String id = (String)attributes.get("registration_id");
return id == null ? this.oauth2AuthorizationException("client_registration_not_found") : this.clientRegistrationRepository.findByRegistrationId(id);
}).switchIfEmpty(this.oauth2AuthorizationException("client_registration_not_found")).map((clientRegistration) -> {
OAuth2AuthorizationResponse authorizationResponse = convertResponse(exchange);
OAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken(clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
return authenticationRequest;
});
}
可能你还会有几个困惑的地方:
OAuth2AuthorizationRequest
对象什么时候放入缓存的;
OAuth2AuthorizationResponse
对象有什么属性。
OAuth2AuthorizationRequest
对象是在经历
OAuth2AuthorizationRequestRedirectWebFilter
过滤器时创建并放入请求的Session里的。
例如使用的是授权码模式,由授权商重定向的URL的参数会有 state和code。
OAuth2AuthorizationResponse
对象就是封装了 state 和 code 参数 。
static OAuth2AuthorizationResponse convert(MultiValueMap<String, String> request, String redirectUri) {
String code = (String)request.getFirst("code");
String errorCode = (String)request.getFirst("error");
String state = (String)request.getFirst("state");
if (StringUtils.hasText(code)) {
return OAuth2AuthorizationResponse.success(code).redirectUri(redirectUri).state(state).build();
} else {
String errorDescription = (String)request.getFirst("error_description");
String errorUri = (String)request.getFirst("error_uri");
return OAuth2AuthorizationResponse.error(errorCode).redirectUri(redirectUri).errorDescription(errorDescription).errorUri(errorUri).state(state).build();
}
}
ServerAuthenticationConverter只是把请求转换成了 Authentication 认证信息对象,没有进行实际的认证授权操作,真正的认证授权都是由 ReactiveAuthenticationManager 来实现的。
自定义ServerAuthenticationConverter
当然,如果我们不想使用默认的实现类,我们也可以自己单独配置。配置如下所示:
@Configuration
@EnableWebFluxSecurity
@Slf4j
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.oauth2Login(oauth2Login -> oauth2Login
// 传入自定义的 ServerAuthenticationConverter 实现类
.authenticationConverter(...)
.build();
}
}