第19章 OAuth2LoginAuthenticationWebFilter 之ServerAuthenticationConverter

  • Post author:
  • Post category:其他


在上一篇中我们分析了 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();
		}
}



版权声明:本文为buffeer原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。