SpringBoot e-commerce project mall (20k+star) address: https://github.com/macrozheng/mall
abstract
Spring Cloud Security provides a series of solutions for building secure SpringBoot applications, and with Oauth2, more functions can be achieved, such as using JWT tokens to store information and refreshing tokens. This article will give a detailed description of how it works with JWT.
Introduction to JWT
JWT is the abbreviation of JSON WEB TOKEN. It is a JSON object that can be safely transferred based on RFC 7519 standard. Because of the use of digital signatures, it is trusted and secure.
Composition of JWT
- Format of JWT token: header.payload.signature;
- Generation algorithm used in header to store signatures;
{ "alg": "HS256", "typ": "JWT" }
- payload is used to store data such as expiration time, user name, user rights, etc.
{ "exp": 1572682831, "user_name": "macro", "authorities": [ "admin" ], "jti": "c1a0645a-28b5-4468-b4c7-9623131853af", "client_id": "admin", "scope": [ "all" ] }
- Signatures are signature s generated with headers and payloads, and once the headers and payloads are tampered with, validation will fail.
JWT instance
- This is a JWT string:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzI2ODI4MzEsInVzZXJfbmFtZSI6Im1hY3JvIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiYzFhMDY0NWEtMjhiNS00NDY4LWI0YzctOTYyMzEzMTg1M2FmIiwiY2xpZW50X2lkIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXX0.x4i6sRN49R6JSjd5hd1Fr2DdEMBsYdC4KB6Uw1huXPg
- The results of the analysis can be found on this website: https://jwt.io/
Create oauth2-jwt-server module
This module is just an extension of the oauth2-server module, which can be copied directly under the extension.
How tokens are stored in oauth2
In the previous section, we stored tokens in memory, so deploying multiple services would cause the problem of not being able to use tokens.
There are two ways to store tokens in Spring Cloud Security to solve this problem, one using Redis and the other using JWT.
Store tokens using Redis
- Add Redis-related dependencies to pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
- Add redis-related configurations in application.yml:
spring: redis: #redis related configuration password: 123456 #Set with Password
- Add a configuration to store tokens in Redis:
/** * Store token configuration using redis * Created by macro on 2019/10/8. */ @Configuration public class RedisTokenStoreConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore redisTokenStore (){ return new RedisTokenStore(redisConnectionFactory); } }
- Specify the storage policy for tokens in the authentication server configuration as Redis:
/** * Authentication Server Configuration * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired @Qualifier("redisTokenStore") private TokenStore tokenStore; /** * Configuration required to use password mode */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userService) .tokenStore(tokenStore);//Configure Token Storage Policy } //Omit Code... }
- Use password mode to get tokens after running the project, accessing the following addresses: http://localhost:9401/oauth/token
- A token acquisition operation reveals that the token has been stored in Redis.
Store tokens using JWT
- Add a configuration that uses JWT to store tokens:
/** * Store configuration of token using Jwt * Created by macro on 2019/10/8. */ @Configuration public class JwtTokenStoreConfig { @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); accessTokenConverter.setSigningKey("test_key");//Configure the secret key used by JWT return accessTokenConverter; } }
- Specify the storage policy for tokens in the authentication server configuration as JWT:
/** * Authentication Server Configuration * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired @Qualifier("jwtTokenStore") private TokenStore tokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private JwtTokenEnhancer jwtTokenEnhancer; /** * Configuration required to use password mode */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userService) .tokenStore(tokenStore) //Configure Token Storage Policy .accessTokenConverter(jwtAccessTokenConverter); } //Omit Code... }
- Use password mode to get tokens after running the project, accessing the following addresses: http://localhost:9401/oauth/token
- Discover that the acquired token has become a JWT token and get access_token https://jwt.io/ You can get the content by parsing it on the website.
{ "exp": 1572682831, "user_name": "macro", "authorities": [ "admin" ], "jti": "c1a0645a-28b5-4468-b4c7-9623131853af", "client_id": "admin", "scope": [ "all" ] }
Extend content stored in JWT
Sometimes we need to extend what's stored in JWT, here we'll extend the data in JWT with a key of enhance and a value of enhance info.
- Inherit TokenEnhancer to implement a JWT content enhancer:
/** * Jwt Content Enforcer * Created by macro on 2019/10/8. */ public class JwtTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> info = new HashMap<>(); info.put("enhance", "enhance info"); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); return accessToken; } }
- Create a JwtTokenEnhancer instance:
/** * Store configuration of token using Jwt * Created by macro on 2019/10/8. */ @Configuration public class JwtTokenStoreConfig { //Omit Code... @Bean public JwtTokenEnhancer jwtTokenEnhancer() { return new JwtTokenEnhancer(); } }
- Configure the JWT content enhancer in the authentication server configuration:
/** * Authentication Server Configuration * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired @Qualifier("jwtTokenStore") private TokenStore tokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private JwtTokenEnhancer jwtTokenEnhancer; /** * Configuration required to use password mode */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> delegates = new ArrayList<>(); delegates.add(jwtTokenEnhancer); //Configure JWT's Content Enhancer delegates.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(delegates); endpoints.authenticationManager(authenticationManager) .userDetailsService(userService) .tokenStore(tokenStore) //Configure Token Storage Policy .accessTokenConverter(jwtAccessTokenConverter) .tokenEnhancer(enhancerChain); } //Omit Code... }
- After running the project, use password mode to get the token, then parse the token and find that it already contains the extended content.
{ "user_name": "macro", "scope": [ "all" ], "exp": 1572683821, "authorities": [ "admin" ], "jti": "1ed1b0d8-f4ea-45a7-8375-211001a51a9e", "client_id": "admin", "enhance": "enhance info" }
Parsing content in JWT in Java
If we need to get information in JWT, we can use a toolkit called jjwt.
- Add dependencies to pom.xml:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>
- Modify the UserController class to use the jjwt tool class to parse the JWT content stored in the Authorization header.
/** * Created by macro on 2019/9/30. */ @RestController @RequestMapping("/user") public class UserController { @GetMapping("/getCurrentUser") public Object getCurrentUser(Authentication authentication, HttpServletRequest request) { String header = request.getHeader("Authorization"); String token = StrUtil.subAfter(header, "bearer ", false); return Jwts.parser() .setSigningKey("test_key".getBytes(StandardCharsets.UTF_8)) .parseClaimsJws(token) .getBody(); } }
- Place the token in the Authorization header and access the following address for information: http://localhost:9401/user/getCurrentUser
refresh token
When using oauth2 in Spring Cloud Security, if the token fails, you can use the refresh token to get access_token again through refresh_token's authorization mode.
- Simply modify the authentication server's configuration and add refresh_token's authorization mode.
/** * Authentication Server Configuration * Created by macro on 2019/9/30. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("admin") .secret(passwordEncoder.encode("admin123456")) .accessTokenValiditySeconds(3600) .refreshTokenValiditySeconds(864000) .redirectUris("http://www.baidu.com") .autoApprove(true) //Automatic Authorization Configuration .scopes("all") .authorizedGrantTypes("authorization_code","password","refresh_token"); //Add Authorization Mode } }
- Use the refresh token mode to get a new token and access the following addresses: http://localhost:9401/oauth/token
Modules used
springcloud-learning └── oauth2-jwt-server -- Use jwt Of oauth2 Certification Test Service
Project Source Address
https://github.com/macrozheng/springcloud-learning
Public Number
mall project In the full series of learning tutorials, focus on the first time public number is available.