Springsecurity-oauth2的版本是2.2.1.RELEASE.java
使用postman進行/oauth/token的時候,服務端Springsecurity是怎麼處理的呢?spring
圖1數據庫
圖2app
圖3dom
上面的圖2和圖3,咱們就會從服務端得到token。ide
來看BasicAuthenticationFilter的實現,以下List-1所示工具
List-1源碼分析
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.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); } 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); } } private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException { byte[] base64Token = header.substring(6).getBytes("UTF-8"); byte[] decoded; try { decoded = Base64.getDecoder().decode(base64Token); } catch (IllegalArgumentException var7) { throw new BadCredentialsException("Failed to decode basic authentication token"); } String token = new String(decoded, this.getCredentialsCharset(request)); int delim = token.indexOf(":"); if (delim == -1) { throw new BadCredentialsException("Invalid basic authentication token"); } else { return new String[]{token.substring(0, delim), token.substring(delim + 1)}; } }
BasicAuthenticationFilter會判斷request頭部是否有Authorization,且該字段的值是否以"Basic "開頭,以後得到"Basic "後面的值,看extractAndDecodeHeader的實現,獲得ClientID和ClientSecrect,以後會調用ClientDetailsService,得到Client及Client secrect的信息。將獲得的Authentication放入SecurityContextHolder.getContext().setAuthentication()放入到Context中,這樣SpringSecurity的FilterChainProxy後續Filter就不會跑出異常,這樣請求就能順利到達處理/oauth/token的EndPoint——看org.springframework.security.oauth2.provider.endpoint.TokenEndpoint。post
List-2ui
@FrameworkEndpoint public class TokenEndpoint extends AbstractEndpoint { ...... @RequestMapping(value = "/oauth/token", method=RequestMethod.POST) public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { if (!(principal instanceof Authentication)) { throw new InsufficientAuthenticationException( "There is no client authentication. Try adding an appropriate authentication filter."); } String clientId = getClientId(principal); ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId); TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient); if (clientId != null && !clientId.equals("")) { // Only validate the client details if a client authenticated during this // request. if (!clientId.equals(tokenRequest.getClientId())) { // double check to make sure that the client ID in the token request is the same as that in the // authenticated client throw new InvalidClientException("Given client ID does not match authenticated client"); } } if (authenticatedClient != null) { oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); } if (!StringUtils.hasText(tokenRequest.getGrantType())) { throw new InvalidRequestException("Missing grant type"); } if (tokenRequest.getGrantType().equals("implicit")) { throw new InvalidGrantException("Implicit grant type not supported from token endpoint"); } if (isAuthCodeRequest(parameters)) { // The scope was requested or determined during the authorization step if (!tokenRequest.getScope().isEmpty()) { logger.debug("Clearing scope of incoming token request"); tokenRequest.setScope(Collections.<String> emptySet()); } } if (isRefreshTokenRequest(parameters)) { // A refresh token has its own default scopes, so we should ignore any added by the factory here. tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE))); } OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest); if (token == null) { throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType()); } return getResponse(token); }
如上List-2所示,到了/oauth/token後,還會再次調用ClientDetailService獲取ClientId和ClientSecrect,以後用咱們請求的幾個參數,構造TokenRequest,這個類就是POJO,沒有什麼。以後用TokenGranter構造OAuth2AccessToken,TokenGranter的OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest)方法,用咱們請求的參數,構造OAuth2AccessToken。TokenGranter間接調用ResourceOwnerPasswordTokenGranter,以後調用ProviderManager,ProviderManager再調用AuthenticationManager,AuthenticationManager調用DaoAuthenticationProvider,從數據庫中獲取用戶信息,以後移除password,以後建立Token。
通過源碼分析,圖3中的access_token是JDK的UUID值,以下List-3中,new DefaultOAuth2AccessToken時,UUID.randoUUID().toString()的值做爲參數傳入。
List-3
public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean { ...... private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request()); if (validitySeconds > 0) { token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); } token.setRefreshToken(refreshToken); token.setScope(authentication.getOAuth2Request().getScope()); return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token; } ......
如List-4所示,先建立OAuth2RefreshToken(是interface,真實是其實現類DefaultOAuth2RefreshToken),在方法createRefreshToken中能夠看到,refresh_token的值也是JDK的UUID,以後在建立OAuth2AccessToken(是interface,真實是其實現類DefaultOAuth2AccessToken),傳入做爲返回客戶端的refresh_token,也就是圖3中的refresh_token,因此有源碼可知,access_token和refresh_token都是JDK的UUID.randomUUID().toString。
List-4
@Transactional public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); OAuth2RefreshToken refreshToken = null; if (refreshToken == null) { refreshToken = createRefreshToken(authentication); } ...... OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); tokenStore.storeAccessToken(accessToken, authentication); ...... return accessToken; } private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) { if (!isSupportRefreshToken(authentication.getOAuth2Request())) { return null; } int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request()); String value = UUID.randomUUID().toString(); if (validitySeconds > 0) { return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); } return new DefaultOAuth2RefreshToken(value); } private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request()); if (validitySeconds > 0) { token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); } token.setRefreshToken(refreshToken); token.setScope(authentication.getOAuth2Request().getScope()); return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token; }
在使用oauth2會遇到clientId、clientSecret、accessTokenValiditySeconds、refreshTokenValiditySeconds、additionalInformation,這些能夠在ClientDetails的實現類BaseClientDetails中看到。
accessTokenValiditySeconds是accessToken過時時間,refreshTokenValiditySeconds是refreshToken過時時間。
OAuth2AccessTokenJackson1Serializer/OAuth2AccessTokenJackson2Serializer用這個作的序列化,OAuth2AccessToken這個類上有註解。OAuth2AccessToken的實現類DefaultOAuth2AccessToken也只是POJO,並沒有額外的邏輯,在序列化到HttpResponse時用了jackson的序列化工具,因此咱們能夠看到返回有access_token、refresh_token字段