Oauth2的資源伺服器核心原始碼分析
阿新 • • 發佈:2021-08-22
資源伺服器核心原始碼分析
OAuth2AuthenticationProcessingFilter
資源伺服器的核心是OAuth2AuthenticationProcessingFilter過濾器。它被插到配置為資源埠的過濾器鏈中,主要功能是獲取請求中攜帶的access_token中,通過access_token提取OAuth2Authentication並存入Spring Security上下文。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final boolean debug = logger.isDebugEnabled(); final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) res; try { //提取請求攜帶的token,構建一個認證的Authentication物件 Authentication authentication = tokenExtractor.extract(request); if (authentication == null) { if (stateless && isAuthenticated()) { if (debug) { logger.debug("Clearing security context."); } SecurityContextHolder.clearContext(); } if (debug) { logger.debug("No token in request, will continue chain."); } } else { request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal()); if (authentication instanceof AbstractAuthenticationToken) { AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication; needsDetails.setDetails(authenticationDetailsSource.buildDetails(request)); } //獲取token攜帶的認證資訊,OAuth2AuthenticationManager主要做了3件事 //1.通過token獲取使用者的OAuth2Authentication物件 //2.驗證訪問資源resourceId是否符合範圍 //3.驗證客戶端訪問的scope Authentication authResult = authenticationManager.authenticate(authentication); if (debug) { logger.debug("Authentication success: " + authResult); } eventPublisher.publishAuthenticationSuccess(authResult); //將當前的Authentication放入到Context中,訪問後面的資源 SecurityContextHolder.getContext().setAuthentication(authResult); } } catch (OAuth2Exception failed) { SecurityContextHolder.clearContext(); if (debug) { logger.debug("Authentication request failed: " + failed); } eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed), new PreAuthenticatedAuthenticationToken("access-token", "N/A")); authenticationEntryPoint.commence(request, response, new InsufficientAuthenticationException(failed.getMessage(), failed)); return; } chain.doFilter(request, response); }
- TokenExtractor的預設實現類BearerTokenExtractor
- AuthenticationManager的預設實現類是OAuth2AuthenticationManager
TokenExtractor
介面的功能是提取請求中包含的access_token,目前只有一個實現類:BearerTokenExtractor,它只用於提取Bearer型別的access_token。請求中攜帶的access_token引數即可以放在HTTP請求頭中,也可以在HTTP請求引數中。核心原始碼如下:
@Override public Authentication extract(HttpServletRequest request) { String tokenValue = extractToken(request); if (tokenValue != null) { PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(tokenValue, ""); return authentication; } return null; } protected String extractToken(HttpServletRequest request) { // 首先從header中解析access_token String token = extractHeaderToken(request); // 然後從request parameter中access_token if (token == null) { logger.debug("Token not found in headers. Trying request parameters."); token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN); if (token == null) { logger.debug("Token not found in request parameters. Not an OAuth2 request."); } else { request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE); } } return token; }
OAuth2AuthenticationManager
public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication == null) { throw new InvalidTokenException("Invalid token (token not found)"); } else { String token = (String)authentication.getPrincipal(); //藉助tokenServices,根據token載入身份資訊 OAuth2Authentication auth = this.tokenServices.loadAuthentication(token); if (auth == null) { throw new InvalidTokenException("Invalid token: " + token); } else { Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds(); if (this.resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(this.resourceId)) { throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + this.resourceId + ")"); } else { this.checkClientDetails(auth); if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) { OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails(); if (!details.equals(auth.getDetails())) { details.setDecodedDetails(auth.getDetails()); } } auth.setDetails(authentication.getDetails()); auth.setAuthenticated(true); return auth; } } } }
其中,最關鍵的tokenServices是ResourceServerTokenServices的例項。ResourceServerTokenServices介面的最主要的2個實現類是RemoteTokenServices和DefaultTokenServices。下面的斷點中,我用的是遠端呼叫,RemoteTokenServices
下面是我部分斷點的圖,比較簡單,就不說明了,其中pig的官網也有說明,連線:pig 校驗令牌詳解 【原理】 · 語雀 (yuque.com)