美文网首页
Spring Security Oauth2 Token 提取流

Spring Security Oauth2 Token 提取流

作者: hdfg159 | 来源:发表于2021-03-31 09:36 被阅读0次

    Spring Security Oauth2 Token 提取流程源码分析

    问题

    Spring Security Oauth2 请求 提取 Token 方式

    • 请求头携带token
    • url参数携带token
    • 请求form表单

    源码

    spring-security-Oauth2 版本: 2.3.4.RELEASE

    配置

    • 访问受保护的资源需要携带token访问,所以资源服务器都会写一个相应的安全配置类
    • 配置适配器类都是继承 ResourceServerConfigurerAdapter,配合使用@EnableResourceServer启用相应资源服务器配置,内部关联了ResourceServerSecurityConfigurerHttpSecurity

    资源安全配置

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
           // 配置代码 省略
        }
    

    Http安全相关

    配置权限地址拦截这些都是使用这个方法参数进行相应的配置),相应代码如下:

        @Override
        public void configure(HttpSecurity http) throws Exception {
            // 配置代码 省略
        }
    

    关键代码分析

    ResourceServerSecurityConfigurer 源码分析

    • ResourceServerSecurityConfigurer是一个配置器,
      关键地方是看他挂载了那些过滤器

    • 方法签名为org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer.configure注册了OAuth2AuthenticationProcessingFilter正是核心的关键,注册在AbstractPreAuthenticatedProcessingFilter之前,详细代码和部分注释如下:

    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
            // 初始化过滤器
            resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
            resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
            resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
            if (eventPublisher != null) {
                resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher);
            }
            // 配置 token 提取实现类
            if (tokenExtractor != null) {
                resourcesServerFilter.setTokenExtractor(tokenExtractor);
            }
            if (authenticationDetailsSource != null) {
                resourcesServerFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
            }
            resourcesServerFilter = postProcess(resourcesServerFilter);
            // 设置无状态
            resourcesServerFilter.setStateless(stateless);
    
            http
                .authorizeRequests().expressionHandler(expressionHandler)
            .and()
                    // 在AbstractPreAuthenticatedProcessingFilter之前插入resourcesServerFilter过滤器
                .addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
                .exceptionHandling()
                        // 配置访问拒绝处理器
                    .accessDeniedHandler(accessDeniedHandler)
                    .authenticationEntryPoint(authenticationEntryPoint);
        }
    

    OAuth2AuthenticationProcessingFilter 源码分析

    • OAuth2AuthenticationProcessingFilter是一个普通实现javax.servlet.Filterorg.springframework.beans.factory.InitializingBean接口的Bean;

    • 既然是过滤器,核心方法当然是org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter
      详细代码如下:

        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
                ServletException {
                    // 获取日志是否开启 debug 模式
            final boolean debug = logger.isDebugEnabled();
            final HttpServletRequest request = (HttpServletRequest) req;
            final HttpServletResponse response = (HttpServletResponse) res;
            try {
                    // 关键代码,提取token返回认证信息
                Authentication authentication = tokenExtractor.extract(request);
                            // 省略代码...
            }
            catch (OAuth2Exception failed) {
    
                     // 省略代码...
                return;
            }
            chain.doFilter(request, response);
        }
    
    • 追溯源码org/springframework/security/oauth/spring-security-oauth2/2.3.4.RELEASE/spring-security-oauth2-2.3.4.RELEASE-sources.jar!/org/springframework/security/oauth2/provider/authentication/OAuth2AuthenticationProcessingFilter.java:64
    • 发现TokenExtractor接口实现类是org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor
    • BearerTokenExtractor详细源码里面先后调用了几个方法,从extract->extractToken方法,extractToken方法是关键代码,详细代码如下:
        protected String extractToken(HttpServletRequest request) {
            // 提取请求头参数
            String token = this.extractHeaderToken(request);
            if (token == null) {
                logger.debug("Token not found in headers. Trying request parameters.");
                // 直接获取请求参数
                token = request.getParameter("access_token");
                if (token == null) {
                    logger.debug("Token not found in request parameters.  Not an OAuth2 request.");
                } else {
                    // 如果两种方式都获取不到,直接设置一个认证请求头,不带 token
                    request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, "Bearer");
                }
            }
    
            return token;
        }
    
        protected String extractHeaderToken(HttpServletRequest request) {
            // 获取请求头为 Authorization 的信息 
            Enumeration headers = request.getHeaders("Authorization");
    
            String value;
            do {
                if (!headers.hasMoreElements()) {
                    return null;
                }
    
                value = (String)headers.nextElement();
            } while(!value.toLowerCase().startsWith("Bearer".toLowerCase()));
            // 如果存在,提取Bear后部分实际token值
            String authHeaderValue = value.substring("Bearer".length()).trim();
            // 重新设置 request 属性
            request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, value.substring(0, "Bearer".length()).trim());
            int commaIndex = authHeaderValue.indexOf(44);
            if (commaIndex > 0) {
                authHeaderValue = authHeaderValue.substring(0, commaIndex);
            }
    
            return authHeaderValue;
        }
    

    从上面代码分析得到:
    extractToken方法是提取token,返回相应的认证信息
    extractHeaderToken是从请求头获取相应 token,extractHeaderToken无法从请求头相应参数获取 token 时候,request.getParameter("access_token");这段代码会获取请求参数。

    结论

    整个流程下来,是通过OAuth2AuthenticationProcessingFilter提取请求头参数,获取不到再去获取请求参数。

    相关文章

      网友评论

          本文标题:Spring Security Oauth2 Token 提取流

          本文链接:https://www.haomeiwen.com/subject/sulohltx.html