Json Web Token (JWT) is an open jSON-based standard (RFC 7519) implemented for the transfer of declarations between network application environments. The token is designed to be compact and secure, especially suitable for single sign-on (SSO) scenarios in distributed sites. The JWT declaration is generally used to pass authenticated user identity information between the identity provider and the service provider to obtain resources from the resource server, and to add some additional declaration information necessary for other business logic. The token can also be used directly for authentication or can be encrypted.

JWT composition

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/24/16123bf985836aaa~tplv-t2oaga2asx-image.image

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE1MTY3MjY4MTMsImJsb2ciOiJ odHRwczovL2xvbmdmZWl6aGVuZy5naXRodWIuaW8vIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6ImJmZmY0NjRjLTFiNTktNGZkNy1hNTE 4LWU3YjY5MDFiNzU3YyIsImNsaWVudF9pZCI6Im1lcnJ5eW91In0.gp5t9nY9mGp5O2-yqdflc0nEAsTeCQG7VugA8q8XcF4Copy the code

Header

The Header contains some metadata that at the very least indicates the token type and the signature method.

{
 "typ": "JWT"."alg": "HS256"
}
Copy the code

Claims (Payload)

The Claims section contains some important information about this token.

{
 "user_name": "admin"."scope": [
  "all"]."exp": 1516726813."blog": "https://longfeizheng.github.io/"."authorities": [
  "ROLE_USER"]."jti": "bfff464c-1b59-4fd7-a518-e7b6901b757c"."client_id": "merryyou"
}
Copy the code

Signature

The JWT standard follows the JSON Web Signature (JWS) standard to generate signatures. The signature is mainly used to verify whether the token is valid or tampered with.

JWT process diagram

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/24/16123bf9857c4ec6~tplv-t2oaga2asx-image.image

Spring Security Oauth2 implements JWT

Configure TokenStoreConfig to store tokens

@Configuration
public class TokenStoreConfig {
    /** * redis connection factory */
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    /** * used to store tokens *@return* /
    @Bean
    @ConditionalOnProperty(prefix = "merryyou.security.oauth2", name = "storeType", havingValue = "redis")
    public TokenStore redisTokenStore(a) {
        return new RedisTokenStore(redisConnectionFactory);
    }

    /** * JWT TOKEN configuration information */
    @Configuration
    @ConditionalOnProperty(prefix = "merryyou.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true)
    public static class JwtTokenCofnig{

        /** * Use jwtTokenStore to store tokens *@return* /
        @Bean
        public TokenStore jwtTokenStore(a){
            return new JwtTokenStore(jwtAccessTokenConverter());
        }

        /** * is used to generate JWT *@return* /
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(a){
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
            accessTokenConverter.setSigningKey("merryyou");// Generate the signature key
            return accessTokenConverter;
        }

        /** * to extend JWT *@return* /
        @Bean
        @ConditionalOnMissingBean(name = "jwtTokenEnhancer")
        public TokenEnhancer jwtTokenEnhancer(a){
            return newMerryyouJwtTokenEnhancer(); }}}Copy the code
MerryyouJwtTokenEnhancer
public class MerryyouJwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String, Object> info = new HashMap<>();
        info.put("blog"."https://longfeizheng.github.io/");// Extend the token returned
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
        returnaccessToken; }}Copy the code

Configuring an Authentication Server

@Configuration
@EnableAuthorizationServer
public class MerryyouAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private TokenStore tokenStore;

    @Autowired(required = false)
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired(required = false)
    private TokenEnhancer jwtTokenEnhancer;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
        // Extend token returns the result
        if(jwtAccessTokenConverter ! =null&& jwtTokenEnhancer ! =null) {
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = new ArrayList();
            enhancerList.add(jwtTokenEnhancer);
            enhancerList.add(jwtAccessTokenConverter);
            tokenEnhancerChain.setTokenEnhancers(enhancerList);
            //jwtendpoints.tokenEnhancer(tokenEnhancerChain) .accessTokenConverter(jwtAccessTokenConverter); }}/** * Configure some information on the client *@param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("merryyou")
                .secret("merryyou")
                .accessTokenValiditySeconds(7200)
                .authorizedGrantTypes("refresh_token"."password"."authorization_code")//OAuth2 supports the authentication mode
                .scopes("all"); }}Copy the code

Configuring a Resource Server

@Configuration
@EnableResourceServer
public class MerryyouResourceServerConfig extends ResourceServerConfigurerAdapter {

    /** * Custom login success handler */
    @Autowired
    private AuthenticationSuccessHandler appLoginInSuccessHandler;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .successHandler(appLoginInSuccessHandler)// Successful login handler.and() .authorizeRequests().anyRequest().authenticated().and() .csrf().disable(); }}Copy the code

Parse extended tokens

 @GetMapping("/user")
    public Object getCurrentUser1(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException {
        log.info("[SecurityOauth2Application] getCurrentUser1 authenticaiton = {}", JsonUtil.toJson(authentication));

        String header = request.getHeader("Authorization");
        String token = StringUtils.substringAfter(header, "bearer ");

        Claims claims = Jwts.parser().setSigningKey("merryyou".getBytes("UTF-8")).parseClaimsJws(token).getBody();
        String blog = (String) claims.get("blog");
        log.info("[SecurityOauth2Application] getCurrentUser1 blog = {}", blog);

        return authentication;
    }
Copy the code

The test method

    @Test
    public void signInTest(a) throws Exception {
        RestTemplate rest = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.add("authorization", getBasicAuthHeader());

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("username"."admin");
        params.add("password"."123456"); HttpEntity<? > entity =new HttpEntity(params, headers);
        // pay attention, if using get with headers, should use exchange instead of getForEntity / getForObject
        ResponseEntity<String> result = rest.exchange(SIGN_IN_URI, HttpMethod.POST, entity, String.class, new Object[]{null});
        log.info("Result returned by login information ={}", JsonUtil.toJson(result));
    }
Copy the code

Print:

 "body": "{\"access_token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE1MT Y3MjkxNDIsImJsb2ciOiJodHRwczovL2xvbmdmZWl6aGVuZy5naXRodWIuaW8vIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6IjMzOTUxND K1LTBjOGYtNGQ5NS1iZDYyLTAxMjEyYWNjZDU1ZCIsImNsaWVudF9pZCI6Im1lcnJ5eW91In0. 7 lrpmn3canweqcmeadjezjgdtezyn - gg5OpAzbKIEqQ \ ", \"token_type\":\"bearer\",\"refresh_token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlI jpbImFsbCJdLCJhdGkiOiIzMzk1MTQ5NS0wYzhmLTRkOTUtYmQ2Mi0wMTIxMmFjY2Q1NWQiLCJleHAiOjE1MTkzMTM5NDIsImJsb2ciOiJodHRwczovL2xvb mdmZWl6aGVuZy5naXRodWIuaW8vIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6IjFlMjI1YzE5LTE5NDMtNGNjMi1iYTdjLTM1MzdmZDA1M 2E4MyIsImNsaWVudF9pZCI6Im1lcnJ5eW91In0.lKHgXd2HSPCp2cK6S-ZvLUwXRjnXEX9wryDWV4CmSGw\",\"expires_in\":7199,\"scope\":\"all \",\"blog\":\"https://longfeizheng.github.io/\",\"jti\":\"33951495-0c8f-4d95-bd62-01212accd55d\"}"
Copy the code

The effect is as follows:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/24/16123bf985aba935~tplv-t2oaga2asx-image.image

The code download

Download it from my Github, github.com/longfeizhen…