javascript
Springsecurity-oauth2之/oauth/token的处理
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
? ? Springsecurity-oauth2的版本是2.2.1.RELEASE.
? ? 使用postman進行/oauth/token的時候,服務(wù)端Springsecurity是怎么處理的呢?
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??圖1
? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖2
? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖3
? ? 上面的圖2和圖3,我們就會從服務(wù)端獲得token。
? ? 來看BasicAuthenticationFilter的實現(xiàn),如下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的實現(xiàn),得到ClientID和ClientSecrect,之后會調(diào)用ClientDetailsService,獲得Client及Client secrect的信息。將得到的Authentication放入SecurityContextHolder.getContext().setAuthentication()放入到Context中,這樣SpringSecurity的FilterChainProxy后續(xù)Filter就不會跑出異常,這樣請求就能順利到達處理/oauth/token的EndPoint——看org.springframework.security.oauth2.provider.endpoint.TokenEndpoint。
? ? List-2
@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 clientthrow 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 stepif (!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后,還會再次調(diào)用ClientDetailService獲取ClientId和ClientSecrect,之后用我們請求的幾個參數(shù),構(gòu)造TokenRequest,這個類就是POJO,沒有什么。之后用TokenGranter構(gòu)造OAuth2AccessToken,TokenGranter的OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest)方法,用我們請求的參數(shù),構(gòu)造OAuth2AccessToken。TokenGranter間接調(diào)用ResourceOwnerPasswordTokenGranter,之后調(diào)用ProviderManager,ProviderManager再調(diào)用AuthenticationManager,AuthenticationManager調(diào)用DaoAuthenticationProvider,從數(shù)據(jù)庫中獲取用戶信息,之后移除password,之后創(chuàng)建Token。
? ? 經(jīng)過源碼分析,圖3中的access_token是JDK的UUID值,如下List-3中,new?DefaultOAuth2AccessToken時,UUID.randoUUID().toString()的值作為參數(shù)傳入。
? ? 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所示,先創(chuàng)建OAuth2RefreshToken(是interface,真實是其實現(xiàn)類DefaultOAuth2RefreshToken),在方法createRefreshToken中可以看到,refresh_token的值也是JDK的UUID,之后在創(chuàng)建OAuth2AccessToken(是interface,真實是其實現(xiàn)類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的實現(xiàn)類BaseClientDetails中看到。
????accessTokenValiditySeconds是accessToken過期時間,refreshTokenValiditySeconds是refreshToken過期時間。
????OAuth2AccessTokenJackson1Serializer/OAuth2AccessTokenJackson2Serializer用這個做的序列化,OAuth2AccessToken這個類上有注解。OAuth2AccessToken的實現(xiàn)類DefaultOAuth2AccessToken也只是POJO,并無額外的邏輯,在序列化到HttpResponse時用了jackson的序列化工具,所以我們可以看到返回有access_token、refresh_token字段
轉(zhuǎn)載于:https://my.oschina.net/u/2518341/blog/3031049
總結(jié)
以上是生活随笔為你收集整理的Springsecurity-oauth2之/oauth/token的处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java return
- 下一篇: OSGI嵌入jetty应用服务器