OAuth is an open standard that allows users to give third-party applications access to private resources (such as photos, videos, and contact lists) stored on a site without having to provide a user name and password to third-party applications. OAuth allows users to provide a token, rather than a username and password, to access the data they store with a particular service provider. Each token grants a specific site access to a specific resource for a specific period of time. In this way, OAuth allows users to authorize third-party sites to access specific information they store with another service provider. See Understanding OAuth 2.0 for more OAuth2

Project preparation

  1. Add the dependent

    <dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-security</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.security.oauth</groupId>
    			<artifactId>spring-security-oauth2</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    		</dependency>
    Copy the code
  2. Configuring an Authentication Server

    @Configuration
    @EnableAuthorizationServer// No, just a note
    public class MerryyouAuthorizationServerConfig {}Copy the code
  3. Configuring a Resource Server

    @Configuration
    @EnableResourceServer// Yes, it is a comment
    public class MerryyouResourceServerConfig {}Copy the code
  4. Configure application. Yml client information (if not, the console prints Clientid and clietSecret by default)

    security:
      oauth2:
    	client:
    	  client-id: merryyou
    	  client-secret: merryyou
    Copy the code
  5. Define MyUserDetailsService

    @Component
    public class MyUserDetailsService implements UserDetailsService {
    
       @Override
       public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       	return new User(username, "123456", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); }}Copy the code
  6. Adding a test class SecurityOauth2Test(User Name and Password mode)

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @Slf4j
    public class SecurityOauth2Test {
    	/ / port
    	final static long PORT = 9090;
    	//clientId
    	final static String CLIENT_ID = "merryyou";
    	//clientSecret
    	final static String CLIENT_SECRET = "merryyou";
    	/ / user name
    	final static String USERNAME = "admin";
    	/ / password
    	final static String PASSWORD = "123456";
    	// Get the URI of accessToken
    	final static String TOKEN_REQUEST_URI = "http://localhost:"+PORT+"/oauth/token? grant_type=password&username=" + USERNAME + "&password=" + PASSWORD+"&scope=all";
    	// Get user information from the URL
    	final static String USER_INFO_URI = "http://localhost:"+PORT+"/user";
    
    	@Test
    	public void getUserInfo(a) throws Exception{
    		RestTemplate rest = new RestTemplate();
    		HttpHeaders headers = new HttpHeaders();
    		headers.add( "authorization"."Bearer " + getAccessToken() );
    		HttpEntity<String> entity = new HttpEntity<String>(null, headers);
    		// pay attention, if using get with headers, should use exchange instead of getForEntity / getForObject
    		ResponseEntity<String> result = rest.exchange( USER_INFO_URI, HttpMethod.GET, entity, String.class, new Object[]{ null}); log.info("Result returned by user information ={}",JsonUtil.toJson(result));
    	}
    
    	/** * get accessToken *@return* /
    	private String getAccessToken(a){
    		RestTemplate rest = new RestTemplate();
    		HttpHeaders headers = new HttpHeaders();
    		headers.setContentType( MediaType.TEXT_PLAIN );
    		headers.add("authorization", getBasicAuthHeader());
    		HttpEntity<String> entity = new HttpEntity<String>(null, headers);
    		ResponseEntity<OAuth2AccessToken> resp = rest.postForEntity( TOKEN_REQUEST_URI, entity, OAuth2AccessToken.class);
    		if( !resp.getStatusCode().equals( HttpStatus.OK )){
    			throw new RuntimeException( resp.toString() );
    		}
    		OAuth2AccessToken t = resp.getBody();
    		log.info("accessToken={}",JsonUtil.toJson(t));
    		log.info("the response, access_token: " + t.getValue() +"; token_type: " + t.getTokenType() +"; "
    				+ "refresh_token: " + t.getRefreshToken() +"; expiration: " + t.getExpiresIn() +", expired when:" + t.getExpiration() );
    		return t.getValue();
    
    	}
    
    	/** * Build header *@return* /
    	private String getBasicAuthHeader(a){
    		String auth = CLIENT_ID + ":" + CLIENT_SECRET;
    		byte[] encodedAuth = Base64.encodeBase64(auth.getBytes());
    		String authHeader = "Basic " + new String(encodedAuth);
    		returnauthHeader; }}Copy the code

The authorization code mode has the following effects:

Authorization link: http://localhost:9090/oauth/authorize? response_type=code&client_id=merryyou&redirect_uri=http://merryyou.cn&scope=all

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/20/161137658100fd03~tplv-t2oaga2asx-image.image

The test class prints accessToken information

18:16:49 2018-01-20. 16136-900 the INFO [main]. Cn merryyou. Security. SecurityOauth2Test: accessToken = {" value ": "8e5ea72c-d153-48f5-8ee7-9b5616fc43dc", "expiration": "Jan 21, 2018 6:10:25 AM", "tokenType": "bearer", "refreshToken": { "value": "7adfefec-c80c-4ff4-913c-4f161c47fbf1" }, "scope": [ "all" ], "additionalInformation": {} }Copy the code

Spring Security OAuth2 login core source code

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/20/1611374a32de17ae~tplv-t2oaga2asx-image.image

TokenEndpoint

//#1. Handle /oauth/token requests
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
	public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map
       
         parameters)
       ,> throws HttpRequestMethodNotSupportedException {

		if(! (principalinstanceof Authentication)) {
			throw new InsufficientAuthenticationException(
					"There is no client authentication. Try adding an appropriate authentication filter.");
		}
		/ / get clientId
		String clientId = getClientId(principal);
		// Obtain detailed configuration information about third-party applications
		ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
		// Create a TokenRequest using third-party application information
		TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
		// There is no clientId
		if(clientId ! =null && !clientId.equals("")) {
			// Only validate the client details if a client authenticated during this
			// request.
			// Check whether it matches the configuration
			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) {
			/ / check the scope
			oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
		}
		// Whether grant_type has a value corresponding to four authorization modes and refresh tokens
		if(! StringUtils.hasText(tokenRequest.getGrantType())) {throw new InvalidRequestException("Missing grant type");
		}
		// Whether to simplify the schema
		if (tokenRequest.getGrantType().equals("implicit")) {
			throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
		}
		// Check whether the authorization code mode is used
		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");
				Scope is set to null according to scope set when code is obtainedtokenRequest.setScope(Collections.<String> emptySet()); }}// Whether to refresh the token
		if (isRefreshTokenRequest(parameters)) {
			// A refresh token has its own default scopes, so we should ignore any added by the factory here.
			/ / set the scope
			tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
		}
		/ / get OAuth2AccessToken
		OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
		if (token == null) {
			throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
		}

		return getResponse(token);

	}
Copy the code

ClientDetails

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/20/1611374a32ef5e11~tplv-t2oaga2asx-image.image

TokenRequest

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/20/1611374a32b81a7f~tplv-t2oaga2asx-image.image

CompositeTokenGranter#grant

	// Four authorization modes + Refreshing token modes according to grant_type
	public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
		for (TokenGranter granter : tokenGranters) {
			OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
			if(grant! =null) {
				returngrant; }}return null;
	}
Copy the code

tokenGranters

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/20/1611374a32b81a7f~tplv-t2oaga2asx-image.image

AbstractTokenGranter#grant

public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
		// Check whether the current authorization type matches the one passed in
		if (!this.grantType.equals(grantType)) {
			return null;
		}
		/ / get clientId
		String clientId = tokenRequest.getClientId();
		ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
		/ / check
		validateGrantType(grantType, client);
		
		logger.debug("Getting access token for: " + clientId);
		// Generate a token
		return getAccessToken(client, tokenRequest);

	}
Copy the code

AbstractTokenGranter#getAccessToken

protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
		return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));
	}
Copy the code

DefaultTokenServices#createAccessToken

public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
		// Get OAuth2AccessToken from tokenStore (if the token exists, the same token will be returned in different authorization modes)
		OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
		OAuth2RefreshToken refreshToken = null;
		// Determine whether it expires
		if(existingAccessToken ! =null) {
			if (existingAccessToken.isExpired()) {
				if(existingAccessToken.getRefreshToken() ! =null) {
					// Delete expired tokens
					refreshToken = existingAccessToken.getRefreshToken();
					// The token store could remove the refresh token when the
					// access token is removed, but we want to
					// be sure...

					tokenStore.removeRefreshToken(refreshToken);
				}
				tokenStore.removeAccessToken(existingAccessToken);
			}
			else {
				// If the token exists, store it again
				// Re-store the access token in case the authentication has changed
				tokenStore.storeAccessToken(existingAccessToken, authentication);
				// Return directly after storage
				returnexistingAccessToken; }}// Only create a new refresh token if there wasn't an existing one
		// associated with an expired access token.
		// Clients might be holding existing refresh tokens, so we re-use it in
		// the case that the old access token
		// expired.
		// Check that the refresh token does not exist
		if (refreshToken == null) {
			// Create a refresh token
			refreshToken = createRefreshToken(authentication);
		}
		// But the refresh token itself might need to be re-issued if it has
		// expired.
		else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
			/ / overdue
			ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
			if(System.currentTimeMillis() > expiring.getExpiration().getTime()) { refreshToken = createRefreshToken(authentication); }}// Create OAuth2AccessToken based on the refresh token
		OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
		tokenStore.storeAccessToken(accessToken, authentication);
		// In case it was modified
		refreshToken = accessToken.getRefreshToken();
		if(refreshToken ! =null) {
			tokenStore.storeRefreshToken(refreshToken, authentication);
		}
		/ / return OAuth2AccessToken
		return accessToken;

	}
Copy the code

The code download

Download it from my Github, github.com/longfeizhen…