Spring boot + oauth2, how to customize the returned token information?

Time:2020-11-25

In the previous articles in this series, under normal circumstances, the access returned by oauth2_ The token information contains five items in total

namely:

  • access_token
  • token_type
  • refresh_token
  • expires_in
  • scope

The details are as follows:

{
    "access_token": "b9c9e345-90c9-49f5-80ab-6ce5ed5a07c9",
    "token_type": "bearer",
    "refresh_token": "9f843e0e-1778-495d-859a-52a1a806c150",
    "expires_in": 7199,
    "scope": "seller-auth"
}

But in practice, we often need to customize our own return information on this basis, which requires us to customize this thing. In this article, SongGe will talk to you about how to customize it.

Hit the blackboard and mark the key points:This article is a continuation of our recent oauth2 series. If you haven’t read the previous articles in this series, you must read them first, which will help us better understand this article

  1. The oauth2 that can’t be bypassed by microservices, SongGe also comes to talk with you
  2. If this case is written out, I’m afraid I can’t understand the oauth2 login process with the interviewer?
  3. Beat oauth2, coach, I want to learn the whole set!
  4. Can oauth2 token be stored in redis? The more you play, the more you slip!
  5. Want oauth2 and JWT to play happily together? Please watch song GE’s performance
  6. Recently, we share our ideas on security management of spring with you
  7. Spring boot + oauth2, single sign on with one annotation!
  8. Let your website access the third party login function of GitHub in minutes

OK, no nonsense. Let’s take a look at today’s content.

1.access_ Where does the token come from

First of all, we need to make it clear, access_ Where does the token come from.

In the previous article, we were generating access_ When token is used, a class called authorization servertoken services is configured, as follows:

@Bean
AuthorizationServerTokenServices tokenServices() {
    DefaultTokenServices services = new DefaultTokenServices();
    services.setClientDetailsService(clientDetailsService());
    services.setSupportRefreshToken(true);
    services.setTokenStore(tokenStore);
    TokenEnhancerChain chain = new TokenEnhancerChain();
    chain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter,externalAccessTokenInfo));
    services.setTokenEnhancer(chain);
    return services;
}

In this configuration, we provide an instance of defaulttokenservices, which generates access by default_ For the tool of token, we can enter the method of defaulttokenservices ා createaccesstoken, and trace it all the way. We can see the following code:

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;
}

From this code, we can see that it is used to save access_ The example of token is actually the defaultoauth2accesstoken. Let’s take a look at the definition of defaultoauth2accesstoken

public class DefaultOAuth2AccessToken implements Serializable, OAuth2AccessToken {
    private String value;
    private Date expiration;
    private String tokenType = BEARER_TYPE.toLowerCase();
    private OAuth2RefreshToken refreshToken;
    private Set<String> scope;
    private Map<String, Object> additionalInformation = Collections.emptyMap();
    //Omit others
}

From the declaration of this property, we can see why there are only five items of data returned by default.

At the same time, we also found that an additionalinformation attribute is provided in the defaultoauth2accesstoken to store additional information. However, we have no way to customize the attributes in the defaultoauth2accesstoken in the defaulttokenservices class. That is to say, by default, we can’t add values to the additionalinformation by ourselves.

Although by default, it can’t be added, but as long as you look at the above source code, you will understand that if we want to customize the return access_ Token information, we need to find a way to define the defaultoauth2accesstoken information.

With the idea, let’s look at the operation.

2. Two customization schemes

As you know, the token information returned in oauth2 can be divided into two categories: opaque token and transparent token.

Opaque token is a kind of unreadable token, which is a common UUID string. The biggest problem with opaque tokens is that they degrade system performance and availability, and increase latency (because tokens must be verified remotely).

The typical representative of transparent token is JWT. The user information is saved in JWT string. For information about JWT, you can refer to this article: do you want oauth2 and JWT to play happily together? Please watch song GE’s performance.

In actual development, in most cases, our oauth2 is used together with JWT, so here I will mainly talk about how to customize the return information in the generated JWT.

If we use the scheme of oauth2 + JWT, under normal circumstances, we need to configure an instance of jwtaccesstokenconverter? The JWT string will be generated by the jwtaccesstokenconverter instance.

Jwtaccesstokenconverter instance generates JWT after the defaulttokenservices ා createaccesstoken method listed above

accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;

That is to say, if accesstokenenhancer is provided, you will enter the enhancer method of accesstokenenhancer to access_ Token is used for secondary processing, and accesstoken enhancer is our jwtaccesstokenhancer instance.

We can see that the first method is to change the time from “create token” to “create token”. However, the second method is to change the time from “create token” to “create token”.

If we adopt the second scheme, we need to customize a class that inherits from jwtaccesstokenconverter, as follows:

public class MyJwt extends JwtAccessTokenConverter {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String, Object> additionalInformation = new LinkedHashMap<>();
        Map<String, Object> info = new LinkedHashMap<>();
        info.put "Author", "a little rain in the south of the Yangtze River");
        info.put("email", "[email protected]");
        info.put("site", "www.javaboy.org");
        info.put("weixin", "a_java_boy2");
        info.put "Wechat official accounts", "a little rain in the south of the Yangtze River");
        info.put("GitHub", "https://github.com/lenve");
        info.put("user", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
        additionalInformation.put("info", info);
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
        return super.enhance(accessToken, authentication);
    }
}

Here, we customize myjwt to inherit from jwtaccesstokenconverter and override the enhance method:

  1. First, we construct our own additional information, which can be obtained from the securitycontextholder if we need the current login user information.
  2. Put the additional information in the additionalinformation attribute of oauth2accesstoken.

This is equivalent to modifying the default generated defaultoauth2accesstoken, and then calling the modified defaultoauth2accesstoken instance super.enhance Method to generate the JWT string, so that the generated JWT string has our custom information.

Finally, configure myjwt instance in tokenconfig, as follows:

@Configuration
public class TokenConfig {
    @Bean
    TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new MyJwt();
        converter.setSigningKey("www.javaboy.org");
        return converter;
    }
}

After the configuration is completed, the code in other places will not change? Let’s start the project to generate the login access_ Token information.

3. Testing

Next, we start the project for testing:

Spring boot + oauth2, how to customize the returned token information?

As you can see, the JWT string generated at this time is relatively long, we will access_ Token get / OAuth / check_ Check the token to find out the specific information generated, as follows:

Spring boot + oauth2, how to customize the returned token information?

As you can see, we have successfully saved the custom information into the JWT string.

Of course, there is another situation that you may just want to add some additional information when calling the / OAuth / token interface, but do not want to add additional information to JWT. This is the effect as follows:

Spring boot + oauth2, how to customize the returned token information?

This effect can be referred to SongGe’s previous article: do you want oauth2 and JWT to play happily together? Please watch song GE’s performance.

4. Expansion

OK, although I shared with you how oauth2 + JWT generates custom access_ Token information, but I believe that after reading, you should also generate custom information for opaque tokens.

I would also like to share with you some ideas:

The core idea of the above code is to add additional information to the additionalinformation property of the defaultoauth2accesstoken object in the process from the defaulttokenservices ා createaccesstoken method to the jwtaccesstokenenhance method.

Jwtaccesstokenhancer is an instance of tokenenhancer, so if we want to customize the information of opaque token, we only need to define our own class to implement the tokenenhancer interface and add additional information to the enhancer method. This idea is for you. You can try it yourself.

Well, let’s share so much with you today. If you feel that you have a harvest, remember to watch and encourage SongGe

Finally, if you find it difficult to read this article, be sure to read the previous articles in this series.

WeChat official accountA little rain in the south of the Yangtze RiverReply 2020 to obtain spring Security + oauth2 dry goods!