美文网首页
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