catalog:
- Custom token configuration
- Replace default token with JWT
- Extended JWT
- Parsing JWT in JAVA
- refresh token
Spring Security allows us to customize the token configuration, such as different clients_ The ID corresponds to different tokens, the effective time of the token, the storage strategy of the token, etc; We can also use JWT to replace the default token.
Custom token configuration
Let's let the authentication server AuthorizationServerConfig inherit the AuthorizationServerConfigurerAdapter and rewrite its configure(ClientDetailsServiceConfigurer clients) method:
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private TokenEnhancer tokenEnhancer; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailService); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("test1") .secret(new BCryptPasswordEncoder().encode("test1111")) .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(864000) .scopes("all", "a", "b", "c") .authorizedGrantTypes("password") .and() .withClient("test2") .secret(new BCryptPasswordEncoder().encode("test2222")) .accessTokenValiditySeconds(7200); } }
After the authentication server inherits the authorization server configureradapter adapter, it needs to override the configure (authorization server endpoints configurer endpoints) method to specify the AuthenticationManager and UserDetailService.
Create a new configuration class SecurityConfig and register the AuthenticationManagerBean we need in it:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean(name = BeanIds.AUTHENTICATION_MANAGER) @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
In addition, overriding the configure(ClientDetailsServiceConfigurer clients) method mainly configures:
- Define two clients_ ID, and the client can use different clients_ ID to obtain different tokens;
- client_ The valid time of the token with ID test1 is 3600 seconds, client_ The valid time of the token with ID test2 is 7200 seconds;
- client_ Refresh with ID test1_ The effective time of the token (described below) is 864000 seconds, that is, 10 days, that is, it can be refreshed within these 10 days_ Token in exchange for a new token;
- Getting client_ When the token ID is test1, the scope can only be specified as a value in all, a, b or c, otherwise the acquisition will fail;
- The client can only be obtained through password mode_ Token with ID test1, while test2 is unlimited.
After starting the project, use the password mode to obtain the token of test1:
image.png
As described earlier, the header needs to pass in the value of test1:test1111 encrypted by base64:
image.png
Return result:
{ "access_token": "3f9864cc-dab5-420a-b129-560f409922d6", "token_type": "bearer", "expires_in": 3599, "scope": "all" }
Replace default token with JWT
To replace the default token with JWT (the default token is generated by UUID), you only need to specify the TokenStore as JwtTokenStore.
Create a JWTokenConfig configuration class:
@Configuration public class JWTokenConfig { @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); accessTokenConverter.setSigningKey("test_key"); // Signature key return accessTokenConverter; } }
The signature key is test_key. After configuring JwtTokenStore in the configuration class, we specify it in the authentication server:
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailService userDetailService; @Autowired private TokenStore jwtTokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .tokenStore(jwtTokenStore) .accessTokenConverter(jwtAccessTokenConverter) .userDetailsService(userDetailService); } ... }
Restart the service to obtain the token, and the system will return the token in the following format:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1Njg3ODkzNTIsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJhZG1pbiJdLCJqdGkiOiI2NzhlZTc5NC1lYzhhLTQxOTYtYmE2NS0xM2QwNjRiOGEyZWUiLCJjbGllbnRfaWQiOiJ0ZXN0MSIsInNjb3BlIjpbImFsbCJdfQ.ojuPewlJ_3hWRwOrlCwcHFZaNIX_78FQwj86Inw79_w", "token_type": "bearer", "expires_in": 3599, "scope": "all", "jti": "678ee794-ec8a-4196-ba65-13d064b8a2ee" }
Set access_ Copy the contents of the token to https://jwt.io/ Website analysis:
image.png
Expand JWT
The PAYLOAD content obtained from the Token parsing above is:
{ "exp": 1568789352, "user_name": "user", "authorities": [ "admin" ], "jti": "678ee794-ec8a-4196-ba65-13d064b8a2ee", "client_id": "test1", "scope": [ "all" ] }
If we want to add some additional information to JWT, we need to implement TokenEnhancer (Token enhancer):
public class JWTTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) { Map<String, Object> info = new HashMap<>(); info.put("message", "hello world"); ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info); return oAuth2AccessToken; } }
We added the message: hello world message to the Token. Then register the Bean in JWTokenConfig:
@Configuration public class JWTokenConfig { ...... @Bean public TokenEnhancer tokenEnhancer() { return new JWTokenEnhancer(); } }
Finally, configure the intensifier in the authentication server:
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailService userDetailService; @Autowired private TokenStore jwtTokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private TokenEnhancer tokenEnhancer; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> enhancers = new ArrayList<>(); enhancers.add(tokenEnhancer); enhancers.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(enhancers); endpoints.authenticationManager(authenticationManager) .tokenStore(jwtTokenStore) .tokenEnhancer(enhancerChain) .accessTokenConverter(jwtAccessTokenConverter) .userDetailsService(userDetailService); } ... }
Restart the project and obtain the token again. The system returns:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU2ODc4OTkzNSwibWVzc2FnZSI6ImhlbGxvIHdvcmxkIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiZDlmM2M4NGItMWNlNy00YWJmLWE1ZjUtODlkMTVmMTA2YTRhIiwiY2xpZW50X2lkIjoidGVzdDEifQ.WjIvH4dpaF8oKXh1qWuSP5o4slgpG9fAWzGTBUrwlA4", "token_type": "bearer", "expires_in": 3599, "scope": "all", "message": "hello world", "jti": "d9f3c84b-1ce7-4abf-a5f5-89d15f106a4a" }
You can see that the returned JSON content has more message information we added, and access will be added_ The token is copied to the jwt.io website for parsing. The contents are as follows:
{ "user_name": "user", "scope": [ "all" ], "exp": 1568789935, "message": "hello world", "authorities": [ "admin" ], "jti": "d9f3c84b-1ce7-4abf-a5f5-89d15f106a4a", "client_id": "test1" }
The parsed JWT also contains the message information we added.
Parsing JWT in Java
To parse JWT in Java code, you need to add the following dependencies:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
Write interface index
@GetMapping("/index") public Object index(HttpServletRequest request) { String header = request.getHeader("Authorization"); String token = StringUtils.substringAfter(header, "bearer "); return Jwts.parser().setSigningKey("test_key".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody(); }
signkey needs to be consistent with the signature key specified in JwtAccessTokenConverter. Restart the project, access / index after obtaining the token, and the output is as follows:
{ "user_name": "user", "scope": [ "all" ], "exp": 1568790296, "message": "hello world", "authorities": [ "admin" ], "jti": "4da297b1-9c12-4251-bf18-93bbc35d25bd", "client_id": "test1" }
refresh token
After the token expires, we can use refresh_token in exchange for a new available token from the system. However, as can be seen from the previous example, the JSON information returned after successful authentication does not contain refresh_token to make the system return refresh_ For token, you need to add the following configuration in the user-defined configuration of the authentication server:
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { ...... @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("test1") .secret(new BCryptPasswordEncoder().encode("test1111")) .authorizedGrantTypes("password", "refresh_token") .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(864000) .scopes("all", "a", "b", "c") .and() .withClient("test2") .secret(new BCryptPasswordEncoder().encode("test2222")) .accessTokenValiditySeconds(7200); } }
Refresh should be added to the authorization method_ In addition to the four standard OAuth2 token obtaining methods, Spring Security OAuth2 internally uses refresh_token is regarded as an extended way to obtain tokens.
Through the above configuration, use test1 as the client_ Refresh will be returned when ID gets token_ token,refresh_ The valid period of a token is 10 days, that is, you can exchange it for a new available token within 10 days.
Restart the project. After successful authentication, the system returns as follows:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU2ODc5MDY4OCwibWVzc2FnZSI6ImhlbGxvIHdvcmxkIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiMGMwZjg3N2ItMmNkYy00NmI2LWJkYTktNThhYmMzMmNkZDQ3IiwiY2xpZW50X2lkIjoidGVzdDEifQ.RmZExfQmbPgCo2UR8HChaTxRoUzkmKB2r2h7quSAUrw", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6IjBjMGY4NzdiLTJjZGMtNDZiNi1iZGE5LTU4YWJjMzJjZGQ0NyIsImV4cCI6MTU2OTY1MTA4OCwibWVzc2FnZSI6ImhlbGxvIHdvcmxkIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNDJmNTQyMjQtNDJiZS00ZWE2LTg5ODktOGE3ZTFhNjkzMjA5IiwiY2xpZW50X2lkIjoidGVzdDEifQ.1MLmQYoh--ExRuuf0_glHApPnTyCBi9UbZoZM3-76Ds", "expires_in": 3599, "scope": "all", "message": "hello world", "jti": "0c0f877b-2cdc-46b6-bda9-58abc32cdd47" }
Suppose now access_ The token has expired. We use refresh_token in exchange for a new token. Send the following request using postman:
image.png
image.png
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU2ODc5MDgxMSwibWVzc2FnZSI6ImhlbGxvIHdvcmxkIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiMjdkOTNkMWQtNzczNi00ODQzLThjOTQtZWI1ZjdkMzIyMWJlIiwiY2xpZW50X2lkIjoidGVzdDEifQ.733DihtA3G3GkfFT82Bu-Z3FYuWHWxX8l_A0hON3XO8", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6IjI3ZDkzZDFkLTc3MzYtNDg0My04Yzk0LWViNWY3ZDMyMjFiZSIsImV4cCI6MTU2OTY1MTA4OCwibWVzc2FnZSI6ImhlbGxvIHdvcmxkIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNDJmNTQyMjQtNDJiZS00ZWE2LTg5ODktOGE3ZTFhNjkzMjA5IiwiY2xpZW50X2lkIjoidGVzdDEifQ.61o51OwW7twZ3tDRzEu__ho0bwwpIgwDPytkpnF00u8", "expires_in": 3599, "scope": "all", "message": "hello world", "jti": "27d93d1d-7736-4843-8c94-eb5f7d3221be" }
Source address: https://github.com/lbshold/springboot/tree/master/Spring-Security-OAuth2-JWT
Author: lconcise
Link: https://www.jianshu.com/p/bca733826e2e
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.