Spring Security Oauth2 刷新token源码分析

刷新token请求接口(/oauth/token)

参数:grant_type、refresh_token、client_id、client_secret
源码解析
1、grant_type为固定值:grant_type=refresh_token
在这里插入图片描述
源码中会根据grant_type是否为refresh_token且refresh_token字段不能为空来判断这个请求是刷新token还是获取token。

2、refresh_token是我们获取token时返回的一个字段用于刷新token
在这里插入图片描述
上图为oauth2.0返回token的字段其中包含refresh_token,我们只需要记住这个值,才有可能刷新token。
3、client_id和client_secret字段的获取
我们可以通过请求头的basic认证来从数据库中获取我们所需要的client_id和client_secret字段。
处理HTTP请求中的BASIC authorization头部,把认证结果写入SecurityContextHolder。
当一个HTTP请求中包含一个名字为Authorization的头部,并且其值格式是Basic xxx时,该Filter会认为这是一个BASIC authorization头部,其中xxx部分应该是一个base64编码的{username}:{password}字符串。比如用户名/密码分别为 admin/admin, 则对应的该头部是 : Basic XXXXXXX 。
该过滤器会从 HTTP BASIC authorization头部解析出相应的用户名和密码然后调用AuthenticationManager进行认证,成功的话会把认证了的结果写入到SecurityContextHolder中SecurityContext的属性authentication上面。同时还会做其他一些处理,比如Remember Me相关处理等等。
如果头部分析失败,该过滤器会抛出异常BadCredentialsException。
如果认证失败,则会清除SecurityContextHolder中的SecurityContext。并且不再继续filter chain的执行
org.springframework.security.web.authentication.www.BasicAuthenticationFilter类中处理过程

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        boolean debug = this.logger.isDebugEnabled();
        String header = request.getHeader("Authorization");
        if (header != null && header.toLowerCase().startsWith("basic ")) {
            try {
                String[] tokens = this.extractAndDecodeHeader(header, request);

                assert tokens.length == 2;

                String username = tokens[0];
                if (debug) {
                    this.logger.debug("Basic Authentication Authorization header found for user '" + username + "'");
                }

                if (this.authenticationIsRequired(username)) {
                    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, tokens[1]);
                    authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
                    Authentication authResult = this.authenticationManager.authenticate(authRequest);
                    if (debug) {
                        this.logger.debug("Authentication success: " + authResult);
                    }
					//此处将basic的认证结果存储到了Authentication。
                    SecurityContextHolder.getContext().setAuthentication(authResult);
                    this.rememberMeServices.loginSuccess(request, response, authResult);
                    this.onSuccessfulAuthentication(request, response, authResult);
                }
            } catch (AuthenticationException var10) {
                SecurityContextHolder.clearContext();
                if (debug) {
                    this.logger.debug("Authentication request for failed: " + var10);
                }

                this.rememberMeServices.loginFail(request, response);
                this.onUnsuccessfulAuthentication(request, response, var10);
                if (this.ignoreFailure) {
                    chain.doFilter(request, response);
                } else {
                    this.authenticationEntryPoint.commence(request, response, var10);
                }

                return;
            }

            chain.doFilter(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

紧接着我们继续看oauth2.0源码
org.springframework.security.oauth2.provider.endpoint.TokenEndpoint中这样写

 @RequestMapping(
        value = {"/oauth/token"},
        method = {RequestMethod.POST}
    )
    public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
    //这里我们将会用到basic认证所存储的对象Authentication
        if (!(principal instanceof Authentication)) {
            throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
        } else {
        //通过Authentication获取clientld
            String clientId = this.getClientId(principal);
            //ClientDetailsService可以通过jdbc连接数据库通过clientid查到我们需要的client_secret字段
            ClientDetails authenticatedClient = this.getClientDetailsService().loadClientByClientId(clientId);
            TokenRequest tokenRequest = this.getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
            if (clientId != null && !clientId.equals("") && !clientId.equals(tokenRequest.getClientId())) {
                throw new InvalidClientException("Given client ID does not match authenticated client");
            } else {
                if (authenticatedClient != null) {
                    this.oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
                }

                if (!StringUtils.hasText(tokenRequest.getGrantType())) {
                    throw new InvalidRequestException("Missing grant type");
                } else if (tokenRequest.getGrantType().equals("implicit")) {
                    throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
                } else {
                    if (this.isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) {
                        this.logger.debug("Clearing scope of incoming token request");
                        tokenRequest.setScope(Collections.emptySet());
                    }

                    if (this.isRefreshTokenRequest(parameters)) {
                        tokenRequest.setScope(OAuth2Utils.parseParameterList((String)parameters.get("scope")));
                    }

                    OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
                    if (token == null) {
                        throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
                    } else {
                        return this.getResponse(token);
                    }
                }
            }
        }
    }

通过clientid获取ClientDetails

 public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        try {
            ClientDetails details = (ClientDetails)this.jdbcTemplate.queryForObject(this.selectClientDetailsSql, new JdbcClientDetailsService.ClientDetailsRowMapper(), new Object[]{clientId});
            return details;
        } catch (EmptyResultDataAccessException var4) {
            throw new NoSuchClientException("No client with requested id: " + clientId);
        }
    }

(源码中我加的注释不要错过哦)至此刷新token所需要的所有字段我们已经都聚齐了,此时只需要调用接口就可以刷新token了!

发布了20 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/SuperBins/article/details/100933290