SpringBoot e-Commerce project mall (20K + STAR) address: github.com/macrozheng/…

Abstract

Spring Cloud Security provides a number of solutions for building secure SpringBoot applications, and Oauth2 provides additional functionality, such as storing information using JWT tokens and refreshing token capabilities, which are described in detail in this article in conjunction with JWT.

JWT profile

JWT is an abbreviation for JSON WEB TOKEN. JWT is a secure JSON object defined by RFC 7519. It is trusted and secure due to the use of digital signatures.

The composition of JWT

  • The JWT token is in the following format: header.payload-signature.
  • Header is used to store the signature generation algorithm;
{
  "alg": "HS256"."typ": "JWT"
}
Copy the code
  • Payload Is used to store data, such as the expiration time, user name, and permission of a user.
{
  "exp": 1572682831."user_name": "macro"."authorities": [
    "admin"]."jti": "c1a0645a-28b5-4468-b4c7-9623131853af"."client_id": "admin"."scope": [
    "all"]}Copy the code
  • Signature indicates the signature generated using header and payload. If the header and payload are tampered with, authentication fails.

JWT instance

  • This is a JWT string:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzI2ODI4MzEsInVzZXJfbmFtZSI6Im1hY3JvIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSw ianRpIjoiYzFhMDY0NWEtMjhiNS00NDY4LWI0YzctOTYyMzEzMTg1M2FmIiwiY2xpZW50X2lkIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXX0.x4i6sRN49R6 JSjd5hd1Fr2DdEMBsYdC4KB6Uw1huXPgCopy the code
  • The results can be obtained at jwt.io/

Create the oauth2-JwT-server module

This module is just an extension of oauth2-server module, which can be directly copied from the extension.

The way tokens are stored in oAuth2

In the previous section we stored the tokens in memory, which would cause problems if multiple services were deployed. There are two ways to store tokens in Spring Cloud Security to solve this problem, one using Redis and the other using JWT.

Use Redis to store tokens

  • Add Redis dependencies to pom.xml:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code
  • Add redis configuration to application.yml:
spring:
  redis: #redis configuration
    password: 123456 Set this parameter when there is a password
Copy the code
  • Add configuration to store tokens in Redis:
/** * Created by macro on 2019/10/8. */
@Configuration
public class RedisTokenStoreConfig {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public TokenStore redisTokenStore (a){
        return newRedisTokenStore(redisConnectionFactory); }}Copy the code
  • Specify Redis as the storage policy for tokens in the 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;

    /** * To use the password mode, you need to configure */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userService)
                .tokenStore(tokenStore);// Configure the token storage policy
    }
    
    // omit code...
}
Copy the code
  • Run the project after use password mode to get the token, visit the following address: http://localhost:9401/oauth/token

  • To obtain the token, you can find that the token has been stored in Redis.

Use JWT to store tokens

  • Add configuration to store tokens using JWT:
/** * Created by macro on 2019/10/8. */
@Configuration
public class JwtTokenStoreConfig {

    @Bean
    public TokenStore jwtTokenStore(a) {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(a) {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("test_key");// Configure the secret key used by JWT
        returnaccessTokenConverter; }}Copy the code
  • Specify the token storage policy as JWT in the 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;

    /** * To use the password mode, you need to configure */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userService)
                .tokenStore(tokenStore) // Configure the token storage policy
                .accessTokenConverter(jwtAccessTokenConverter);
    }
    
    // omit code...
}
Copy the code
  • Run the project after use password mode to get the token, visit the following address: http://localhost:9401/oauth/token

  • It is found that the obtained token has become a JWT token. Take the access_token to https://jwt.io/ and parse it to obtain the content.
{
  "exp": 1572682831."user_name": "macro"."authorities": [
    "admin"]."jti": "c1a0645a-28b5-4468-b4c7-9623131853af"."client_id": "admin"."scope": [
    "all"]}Copy the code

Extend what is stored in JWT

Sometimes we need to extend what is stored in the JWT. Here we extend the JWT with a key of enhance and value of Enhance info.

  • Implement a JWT content enhancer from TokenEnhancer:
/** * Jwt content enhancement * 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);
        returnaccessToken; }}Copy the code
  • Create a JwtTokenEnhancer instance:
/** * Created by macro on 2019/10/8. */
@Configuration
public class JwtTokenStoreConfig {
    
    // omit code...

    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer(a) {
        return newJwtTokenEnhancer(); }}Copy the code
  • Configure JWT content enhancer in 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;

    /** * To use the password mode, you need to configure */
    @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 the token storage policy
                .accessTokenConverter(jwtAccessTokenConverter)
                .tokenEnhancer(enhancerChain);
    }

    // omit code...
}
Copy the code
  • After running the project, use password mode to get the token, and then parse the token to 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"
}
Copy the code

Parse the content in JWT in Java

If we need to get information from 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>
Copy the code
  • Modify the UserController class to use the JJWT utility 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(); }}Copy the code
  • Token inAuthorizationIn the header, access the following address for information:http://localhost:9401/user/getCurrentUser

The refresh token

When using oAuth2 in Spring Cloud Security, if the token is invalid, the access_token can be obtained again using the refresh token in the authorization mode of refresh_Token.

  • You only need to modify the authentication server configuration and add the authorization mode refresh_token.
/** * 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}}Copy the code
  • Use the refresh token mode to get new token, visit the following address: http://localhost:9401/oauth/token

The module used

Springcloud - learning └ ─ ─ oauth2 - JWT - server-- Use JWT's OAUTH2 authentication testing service
Copy the code

Project source code address

Github.com/macrozheng/…

The public,

Mall project full set of learning tutorials serialized, attention to the public number the first time access.