SpringCloud Alibaba microservice combat 17-JWT certification

Overview

After the system returns OAuth2 authentication token information into two categories: an opaque token (opaque tokens) and a transparent token (not opaque tokens).

Opaque token is a kind of non-readable token, generally speaking, it is an ordinary UUID string. Using an opaque token will reduce system performance and availability, and increase latency, because the resource service does not know what the token is and who it represents. It needs to call the authentication server to obtain the user information interface to know who the token is.
The following is our configuration in the resource server, we need to specify the interface address of the authentication server.

security:
  oauth2:
    resource:
      user-info-uri: http://localhost:5000/user/current/get
      id: account-service

A typical representative of a transparent token is JWT. User information is stored in the JWT string. The resource server can parse the token itself and no longer need to go to the authentication server to verify the token.

In the previous chapter, we used the opaque token access_token, but considering that this centralized authorization service will become a bottleneck in the microservice system, in this chapter we will use jwt to replace the previous access_token, special (zhuang) industry (bi ) Point is called decentralization.

Pretend to be

What is jwt

Json web token (JWT) is an open standard (RFC 7519) based on JSON that is implemented to transfer claims between web application environments. The token is designed to be compact and secure, especially suitable for single sign-on (SSO) scenarios of distributed sites. JWT declarations are generally used to transfer the authenticated user identity information between the identity provider and the service provider, so that resources can be obtained from the resource server, and some additional declaration information necessary for other business logic can also be added. The token is also It can be used directly for authentication or encrypted.

Simply put, it is a fixed format string, usually encrypted;

It consists of three parts, the header, payload and signature. These three parts are all in json format.

  • Header: JSON describes the basic information of JWT, such as type and signature algorithm. Use Base64 to encode as a string
  • Payload: JWT information is described in JSON format. In addition to the standard definition, you can also add custom information. Also use Base64 encoding as a string.
    1. iss: Issuer
    2. sub: user
    3. aud: receiver
    4. exp(expires): expiration time described by unix timestamp
    5. iat(issued at): the issuance time described by the unix timestamp
  • Signature: After concatenating the first two strings, use the encryption algorithm defined in the header, use the key to sign, and append the signature information to the end.

JWT can use symmetric encryption keys, but it is safer to use asymmetric keys. This article uses symmetric encryption.

Code modification

database

When we originally used access_token, we created 7 data tables related to oauth2

Insert picture description here

If you use jwt, you only need to store the client information in the database, so we only need to keep the data table oauth_client_details.
Insert picture description here

The other data tables are no longer needed and can be deleted.

Authentication Service AuthorizationServerConfig

  1. Modify the AuthorizationServerConfigconfiguration of the TokenStore
@Bean
public TokenStore tokenStore() {
    
    
    //return new JdbcTokenStore(dataSource);
        return new JwtTokenStore(jwtTokenEnhancer());
}


/**
 * JwtAccessTokenConverter
 * TokenEnhancer的子类,帮助程序在JWT编码的令牌值和OAuth身份验证信息之间进行转换。
 */
 @Bean
 public JwtAccessTokenConverter jwtTokenEnhancer(){
    
    
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // 设置对称签名
        converter.setSigningKey("javadaily");
        return converter;
}

Before, we stored the access_token in the database. After using jwt, we no longer need to store it in the database, so we need to modify the storage method.

jwt need for information using an encryption algorithm signature, here we use the first symmetrical secret key (javadaily) to sign our token, of course, also a symmetric secret key that the server resources also need to use the same secret key.

  1. Modification configure(AuthorizationServerEndpointsConfigurer endpoints)method, the configuration jwt
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    
    
        //如果需要使用refresh_token模式则需要注入userDetailService
        endpoints.authenticationManager(this.authenticationManager)
                        .userDetailsService(userDetailService)
                        .tokenStore(tokenStore())
                        .accessTokenConverter(jwtTokenEnhancer());
}

Here is mainly injection accessTokenConverter, that is , the token converter configured above.

After the above configuration, the authentication server can already generate the jwt token for us. Here we first use Postman to call it to see the generated jwt token.

Insert picture description here

From the above figure, we can see that the jwt token has been generated normally, and we can take the generated jwt token to https://jwt.io/ for analysis.

If we are not very understanding of the generation jwt token of logic, you can DefaultTokenServices#createAccessToken(OAuth2Authentication authentication)and JwtAccessTokenConverter#enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)make a break point on, observe the effect of code execution.

  1. 删除认证服务器提供给资源服务器获取用户信息的接口
/**
 * 获取授权的用户信息
 * @param principal 当前用户
 * @return 授权信息
 */@GetMapping("current/get")
public Principal user(Principal principal){
        return principal;
}

After using the transparent token jwt token, the resource server can directly parse and verify the token, and no longer need to call the authentication server interface, so it can be deleted directly here.

  1. Modify the validity period of jwt token (optional)

    The default period is 12 hours jwt token, refresh token is valid for 30 days, if you want to modify the default time may be injected into DefaultTokenServicesand modify the effective time.

@Primary
@Bean
public DefaultTokenServices tokenServices(){
    
    
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenEnhancer(jwtTokenEnhancer());
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);
        //设置token有效期,默认12小时,此处修改为6小时   21600
        tokenServices.setAccessTokenValiditySeconds(60 * 60 * 6);
        //设置refresh_token的有效期,默认30天,此处修改为7天
        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
        return tokenServices;
}

Then configure()add tokenServices method

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    
    
                //如果需要使用refresh_token模式则需要注入userDetailService
        endpoints.authenticationManager(this.authenticationManager)
                        .userDetailsService(userDetailService)
            //注入自定义的tokenservice,如果不使用自定义的tokenService那么就需要将tokenServce里的配置移到这里
                        .tokenServices(tokenServices());
}

Resource Server ResourceServerConfig

  1. Delete the interface properties of the authentication server configured in the resource server user-info-uri
security:  
  oauth2:    
    resource:      
      id: account-service
  1. Inject TokenStore and JwtAccessTokenConverter
@Bean
public TokenStore tokenStore() {
    
    
        return new JwtTokenStore(jwtTokenEnhancer());
}

@Bean
public JwtAccessTokenConverter jwtTokenEnhancer(){
    
    
        JwtAccessTokenConverter jwtTokenEnhancer = new JwtAccessTokenConverter();
        jwtTokenEnhancer.setSigningKey("javadaily");
        return jwtTokenEnhancer;
}

Note: The symmetric encryption algorithm needs to be consistent with the authentication server secret key. Of course, it can be extracted into the configuration file.

  1. Add configure(ResourceServerSecurityConfigurer resources)method, adding token configuration
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(resourceId)
                        .tokenStore(tokenStore());
}

Question: Where can the token be verified after the resource server uses jwt?

Adding to the application @EnableResourceServerafter the Spring Security annotation will add a FilterChan OAuth2AuthenticationProcessingFilter, OAuth2AuthenticationProcessingFilterwill be used OAuth2AuthenticationManagerto verify the token.

The main code execution sequence of the verification logic is as follows:

Insert picture description here

I suggest that you OAuth2AuthenticationProcessingFilter#doFilter()make a break to experience the verification process at.

Gateway SecurityConfig

  1. Create a ReactiveJwtAuthenticationManagerload OAuth2AccessToken from tokenStore

Since the original access_token is stored in the database, so we wrote ReactiveJdbcAuthenticationManagerto get the access_token from the database, now jwt We also need to define the relevant class jwt of ReactiveJwtAuthenticationManagercode with ReactiveJdbcAuthenticationManagerthe same, there is no longer posted.

  1. Inject TokenStore and JwtAccessTokenConverter
@Bean  
public TokenStore tokenStore() {
    
    
        return new JwtTokenStore(jwtTokenEnhancer());
}

@Bean  
public JwtAccessTokenConverter jwtTokenEnhancer(){
    
    
        JwtAccessTokenConverter jwtTokenEnhancer = new JwtAccessTokenConverter();
        jwtTokenEnhancer.setSigningKey("javadaily");
        return jwtTokenEnhancer;
}

Note: The symmetric encryption algorithm needs to be consistent with the authentication server secret key. Of course, it can be extracted into the configuration file.

  1. Modification SecurityConfig#SecurityWebFilterChain()method, replacingReactiveJdbcAuthenticationManager
ReactiveAuthenticationManager tokenAuthenticationManager 
= new ReactiveJwtAuthenticationManager(tokenStore());

Just pass the tokenStore into the constructor.

test

Test it yourself.

summary

The biggest difference between using jwt token and access_token is that the resource server no longer needs to authenticate the server to verify the token, which improves the overall performance of the system. The process architecture of the project after using jwt is as follows:

Insert picture description here

This series of articles is now the first 19, if you are interested to be the venue of the article before http://javadaily.cn/tags/SpringCloudView

Using jwt, we must not only see the advantages of jwt, but also its shortcomings, so that we can freely choose according to the actual scene, the following are the two biggest shortcomings of jwt:

  • Jwt is a one-time, once the token is issued, it is valid before the expiration time and cannot be discarded. If you modify the user permissions halfway and need to update the information, you can only re-issue a jwt, but the old jwt can still be used normally, and the information obtained by using the old jwt is out of date.
  • jwt contains authentication information. Once leaked, anyone can get all the permissions of the token. In order to prevent misappropriation, the effective time of jwt should not be set too long.

Guess you like

Origin blog.csdn.net/jianzhang11/article/details/107242131