• 第19章 OAuth2LoginAuthenticationWebFilter 之ServerAuthenticationConverter


    在上一篇中我们分析了 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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;
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可能你还会有几个困惑的地方: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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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();
    		}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    车道线检测(一)——PINet论文阅读
    WebGL 与 WebGPU比对[5] - 渲染计算的过程
    qt对于一个QWidget的所有事件
    Vue14 监视属性简写
    事件的事后调查
    qt example plugandpaint 插件 动态库 pnp_extrafiltersd.dll无法加载问题
    SystemC入门学习-第3章 数据类型
    燃气安全如何保障?万宾燃气管网监测系统时刻感知管网运行态势
    C++11
    好用的跨平台同步笔记工具,手机和电脑可同步的笔记工具
  • 原文地址:https://blog.csdn.net/buffeer/article/details/126332582