Analysis of token acquisition process of Spring Security Oauth2

Posted by mashnote on Mon, 10 Jan 2022 15:45:00 +0100

preface

This paper mainly studies the token acquisition process of spring security oauth2. There are five ways to obtain tokens, of which the authorization mode is the most complex. Therefore, this paper studies the token acquisition based on the authorization mode. Without much nonsense, let's go to the core source code flow chart first!

TokenEndpoint: it is the entry controller, that is, the interface where we request / oauth/token to return the token

ClientDetailsService: This is a bit similar to the UserDetailsService in spring security. UserDetailsService reads user information, and
ClientDetailsService reads the client information, that is, according to the username and password stored in Authorization when we send the / oauth/token request. Note that this is not the user's but the client's endpoint information

ClientDetails: This is used to store the client information queried by ClientDetailsService

TokenRequest: This is also used to encapsulate some other information in the request, such as grant_type,client_id, and client details will also be put into it

TokenGrande: this interface encapsulates the default authorization mode in 5 provided by spring security oauth2. This interface will be based on the incoming grant_type implements different authorization logic. No matter which authorization mode is adopted here, two objects OAuth2Request and Authentication will be generated. Finally, the two objects will be combined into oauthauthentication

OAuth2Request: This is to integrate the information of ClientDetails and TokenRequest

Authentication: This is to store the current authorized login user information, which is actually the user information obtained from UserDetailsService

OAuth2Authentication: this object is the information of the currently authorized login user, the currently authorized client information, the authorization mode, and some other parameters in the authorization. Finally, these data will be encapsulated in this object

AuthorizationServerTokenServices: this interface actually uses the assembled oauthauthentication to generate tokens according to the TokenEnhance generation policy and store tokens according to the TokenStore storage method

OAuth2AccessToken: This is the final returned Token information

1, TokenEndpoint

1.1 TokenEndpoint attribute
@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {

	private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();

	private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));

	private WebResponseExceptionTranslator<OAuth2Exception> providerExceptionHandler = new DefaultWebResponseExceptionTranslator();

	private TokenGranter tokenGranter;

	private ClientDetailsService clientDetailsService;

	private OAuth2RequestFactory oAuth2RequestFactory;

	private OAuth2RequestFactory defaultOAuth2RequestFactory;
}
1.2 TokenEndpoint initialization

The initialization of TokenEndpoint occurs in the configuration class AuthorizationServerEndpointsConfiguration, which is imported from the annotation @ EnableAuthorizationServer

@Configuration
@Import(TokenKeyEndpointRegistrar.class)
public class AuthorizationServerEndpointsConfiguration {

	private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer();

	@Autowired
	private ClientDetailsService clientDetailsService;

	@Autowired
	private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();

	@Bean
	public TokenEndpoint tokenEndpoint() throws Exception {
		TokenEndpoint tokenEndpoint = new TokenEndpoint();
		tokenEndpoint.setClientDetailsService(clientDetailsService);//Direct injection
		tokenEndpoint.setProviderExceptionHandler(exceptionTranslator());
		tokenEndpoint.setTokenGranter(tokenGranter());
		tokenEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
		tokenEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
		tokenEndpoint.setAllowedRequestMethods(allowedTokenEndpointRequestMethods());
		return tokenEndpoint;
	}
1.3 postAccessToken

Because the url to get the token is / oauth/token, he will enter the following code

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {

	private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();

	private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));


	@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
	public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
	Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        
        //The interface must be authenticated
		if (!(principal instanceof Authentication)) {throw new InsufficientAuthenticationException("");}
        
        //Remove the clientId from the request header
		String clientId = getClientId(principal);
		//Read database endpoint information through ClientDetailsService
		ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
        //Encapsulate endpoint information + request other parameters
		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 is requested or determined in the authorization step
			if (!tokenRequest.getScope().isEmpty()) {
				tokenRequest.setScope(Collections.<String> emptySet());
			}
		}

		if (isRefreshTokenRequest(parameters)) {
			// The refresh token has its own default scope, so we should ignore any tags added here by the factory.
			tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
		}
        //Core method: get the token and assemble it into OAuth2AccessToken
		OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
		if (token == null) {
			throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
		}
		return getResponse(token);
	}
}

Topics: Java Spring http