Spring Cloud oauth2 check_token source code parsing

Check_token Core class flowchart

The core class of check_toknen is shown below

The process begins with OAuth2AuthenticationProcessingFilter filter

  • Current client requested, OAuth2AuthenticationProcessingFilter can intercept the request, and then through the token tokenExtractor to extract request, construct an Authentication object

  • And then call the OAuth2AuthenticationManager authenticate () method, The main process in authenticate() is to call RemoteTokenServices to authenticate the token and return the OAuth2Authentication object,

  • RemoteTokenServices mainly invoke the authentication server to verify the token and obtain the token information by calling the /oauth/check_token endpoint of the authentication server CheckTokenEndpoint. Then call the extractAuthentication DefaultAccessTokenConverter () method to obtain user information, and through the token information construct OAuth2Request object, Construct OAuth2Authentication based on user information and OAuth2Request object and return it

  • ExtractAuthentication w () method is how to get the user information, it is through the adjustable DefaultUserAuthenticationConverter extractAuthentication () method, The extractAuthentication() method will determine whether the token information contains the user name, if it does not, it will directly return null. If it does, it will get the user permission information in the token information, and then judge whether userDetailsService is empty. If it is empty, the user name is returned. If it is not empty, the loadUserByUsername() method in userDetailsService is called to obtain the user information, which is encapsulated as Authentication information and returned

The source code parsing

Check_token check_token check_token check_token check_token

  • OAuth2AuthenticationProcessingFilter will intercept request, then through tokenExtractor to extract request the token, construct an Authentication object
  • Call the authenticationManager. Authenticate authentication token (authentication) method, get the user information and client information encapsulated into authentication object in the SecurityContextHolder
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 {// Use the tokenextractor. extract(request) method to obtain the token in the request and return the Authentication object 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)); } / / call the authenticationManager. Authenticate (authentication) authentication token, Returns the Authentication Authentication authResult = the authenticationManager. Authenticate (Authentication); if (debug) { logger.debug("Authentication success: " + authResult); } eventPublisher.publishAuthenticationSuccess(authResult); // The check_token process is complete. The user information and client information have been obtained. The information into SecurityContextHolder. GetContext () 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); }Copy the code

Let us together to parse the authenticationManager. Authenticate (authentication) the source code, the authenticationManager has seven implementation class, This class is used by check_token OAuth2AuthenticationManager

OAuth2AuthenticationManager. Authenticate () and did some what things

  • . First of all it takes to token, by calling the tokenServices loadAuthentication (token) got a OAuth2Authentication object, OAuth2Authentication object contains the user information and client information

  • Then the client information obtained in OAuth2Authentication is authenticated

public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication ==  null) { throw new InvalidTokenException("Invalid token (token not found)"); } / / access token String token = (String) authentication. GetPrincipal (); // Use tokenServices to call the authentication resource server on the /oauth/check_token endpoint, Get token authentication token information and client information OAuth2Authentication auth = tokenServices. LoadAuthentication (token); if (auth == null) { throw new InvalidTokenException("Invalid token: " + token); } // Verify client resource id Collection<String> resourceIds = auth.getoAuth2Request ().getResourceids (); if (resourceId ! = null && resourceIds ! = null && ! resourceIds.isEmpty() && ! resourceIds.contains(resourceId)) { throw new OAuth2AccessDeniedException("Invalid token does not contain resource id ("  + resourceId + ")"); } // Check client scope information checkClientDetails(auth); if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) { OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails(); // Guard against a cached copy of the same details if (! details.equals(auth.getDetails())) { // Preserve the authentication details from the one loaded by token services details.setDecodedDetails(auth.getDetails()); } } auth.setDetails(authentication.getDetails()); auth.setAuthenticated(true); return auth; } private void checkClientDetails(OAuth2Authentication auth) { if (clientDetailsService ! = null) { ClientDetails client; try { client = clientDetailsService.loadClientByClientId(auth.getOAuth2Request().getClientId()); } catch (ClientRegistrationException e) { throw new OAuth2AccessDeniedException("Invalid token contains invalid client id"); } Set<String> allowed = client.getScope(); for (String scope : auth.getOAuth2Request().getScope()) { if (! allowed.contains(scope)) { throw new OAuth2AccessDeniedException( "Invalid token contains disallowed scope (" + scope + ") for this client"); }}}}Copy the code

Take us reveal tokenServices. LoadAuthentication (token) how to verify that token, and get a token information

  • Was based on ResourceServerTokenServices RemoteTokenServices implementation class to interpretation, RemoteTokenServices uses HTTP to access the remote interface to verify the token and obtain the token information. /oauth/check_token endpoint = /oauth/check_token endpoint = /oauth/ oauth

  • Then call tokenConverter. ExtractAuthentication (map) for conversion, and get the user information token

@Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {// Encapsulate Token parameter MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>(); formData.add(tokenName, accessToken); HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret)); Map<String, Object> Map = postForMap(checkTokenEndpointUrl, formData, headers); if (map.containsKey("error")) { if (logger.isDebugEnabled()) { logger.debug("check_token returned error: " + map.get("error")); } throw new InvalidTokenException(accessToken); } // gh-838 if (! Boolean.TRUE.equals(map.get("active"))) { logger.debug("check_token returned active attribute: " + map.get("active")); throw new InvalidTokenException(accessToken); } return tokenConverter.extractAuthentication(map); } private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) { if (headers.getContentType() == null) { headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); } @SuppressWarnings("rawtypes") Map map = restTemplate.exchange(path, HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, headers), Map.class).getBody(); @SuppressWarnings("unchecked") Map<String, Object> result = map; return result; }Copy the code

TokenConverter. ExtractAuthentication (map) is how to convert the token information

  • Call DefaultUserAuthenticationConverter. ExtractAuthentication (map) method for user information back to the Authentication object

  • Obtain the client information in the token, and construct the OAuth2Request object according to the client information

  • OAuth2Authentication object is a summary object of user information, request parameters, and client parameters

/ / tokenConverter extractAuthentication (map) method public OAuth2Authentication extractAuthentication (map < String,? > map) { Map<String, String> parameters = new HashMap<String, String>(); Set<String> scope = extractScope(map); / / call DefaultUserAuthenticationConverter. ExtractAuthentication (map) method to get the Authentication user = user information back to the Authentication object userTokenConverter.extractAuthentication(map); String clientId = (String) map.get(clientIdAttribute); parameters.put(clientIdAttribute, clientId); if (includeGrantType && map.containsKey(GRANT_TYPE)) { parameters.put(GRANT_TYPE, (String) map.get(GRANT_TYPE)); } Set<String> resourceIds = new LinkedHashSet<String>(map.containsKey(AUD) ? getAudience(map) : Collections.<String>emptySet()); Collection<? extends GrantedAuthority> authorities = null; if (user==null && map.containsKey(AUTHORITIES)) { @SuppressWarnings("unchecked") String[] roles = ((Collection<String>)map.get(AUTHORITIES)).toArray(new String[0]); authorities = AuthorityUtils.createAuthorityList(roles); } OAuth2Request request = new OAuth2Request(parameters, clientId, authorities, true, scope, resourceIds, null, null, null); return new OAuth2Authentication(request, user); } private Collection<String> getAudience(Map<String, ? > map) { Object auds = map.get(AUD); if (auds instanceof Collection) { @SuppressWarnings("unchecked") Collection<String> result = (Collection<String>) auds; return result; } return Collections.singleton((String)auds); } private Set<String> extractScope(Map<String, ? > map) { Set<String> scope = Collections.emptySet(); if (map.containsKey(scopeAttribute)) { Object scopeObj = map.get(scopeAttribute); if (String.class.isInstance(scopeObj)) { scope = new LinkedHashSet<String>(Arrays.asList(String.class.cast(scopeObj).split(" "))); } else if (Collection.class.isAssignableFrom(scopeObj.getClass())) { @SuppressWarnings("unchecked") Collection<String> scopeColl = (Collection<String>) scopeObj; scope = new LinkedHashSet<String>(scopeColl); // Preserve ordering } } return scope; }Copy the code

Below is userTokenConverter extractAuthentication (map)

public Authentication extractAuthentication(Map<String, ? > map) {if (map.containsKey(USERNAME)) {Object Principal = map.get(USERNAME); // Obtain the token information user resource permission Collection<? extends GrantedAuthority> authorities = getAuthorities(map); //userDetailsService == null Directly sets the user information to the user name //userDetailsService! = null callback userDetailsService. LoadUserByUsername information query to the user according to the user name if (userDetailsService! = null) { UserDetails user = userDetailsService.loadUserByUsername((String) map.get(USERNAME)); authorities = user.getAuthorities(); principal = user; } return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities); } return null; } private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ? > map) { if (! map.containsKey(AUTHORITIES)) { return defaultAuthorities; } Object authorities = map.get(AUTHORITIES); if (authorities instanceof String) { return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities); } if (authorities instanceof Collection) { return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils .collectionToCommaDelimitedString((Collection<?>) authorities)); } throw new IllegalArgumentException("Authorities must be either a String or a Collection"); }Copy the code