使用JWT替換默認令牌token

JSON Web Token (JWT)

自包含

token的創建

org.springframework.security.oauth2.provider.token.DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication, org.springframework.security.oauth2.common.OAuth2RefreshToken)

在這裏插入圖片描述

總結

可見token根據UUID生成的
所以token裏面的信息是無意義的
看上節redis裏面token的存儲,是把相關信息與token做了關聯
如過redis服務器掛掉了,那麼這個token就是毫無意義的,以爲他本身不包含任何信息

jwt 恰恰相反,它是自包含了相關的信息

密籤

密籤的意思 不是 jwt 信息的加密或解密

他是指如果 jwt 裏面的信息被修改以後 我們能夠通過密簽發現

jwt不能存儲敏感信息

可擴展

可以在裏面放任何數據

替換默認token

TokenStoreConfig

這個配置裏面配置了兩個TokenStore
根據不同的配置確認token的類型
默認是JwtTokenStore

@Configuration
public class TokenStoreConfig {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    @ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "redis",matchIfMissing = false)
    public TokenStore tokenStore(){
        return new RedisTokenStore(redisConnectionFactory);
    }

    @Configuration
    @ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "jwt",matchIfMissing = true)
    //prefix 是隻application配置文件中 配置的最後一個點分隔後前面的一部分 這叫前綴
    //name  對應的是後綴 最後一個點分隔後的後面的那部分
    //即檢查的配置項爲 whale.security.oauth2.storeType
    //havingValue = "jwt"  當配置項的值爲jwt的時候 這個類裏面的所有配置生效
    //matchIfMissing 如果配置文件裏沒有找到這個屬性,即默認生效
    public static class JwtTokenConfig{

        @Autowired
        private SecurityProperties securityProperties;

        //token 的存儲
        @Bean
        public TokenStore jwtTokenStore(){
            return new JwtTokenStore(jwtAccessTokenConverter());
        }

        //token生成處理
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
            jwtAccessTokenConverter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey());
            return  jwtAccessTokenConverter;
        }
    }

}

WhaleAuthenticationServiceConfig

在這裏插入圖片描述

OAuth2Properties

/**
     * 這個是祕鑰 一定要保存好
     *
     * 發出去的令牌要用它簽名
     * 收到的令牌也要用它來驗籤
     *
     * jwt 是token的標準協議,唯一的安全性就是這個祕鑰
     *
     */
    private String jwtSigningKey = "whale";

測試 jwt

用表單登錄獲取token的方式
發現 這次的 access_token 很長
在這裏插入圖片描述

"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTg2MDg5NjQsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiMzYzNjJiZWUtMjFjOS00ZWM3LWIxMmUtYjQyYzYyYzViZjI5IiwiY2xpZW50X2lkIjoid2hhbGUiLCJzY29wZSI6WyJhbGwiLCJyZWFkIiwid3JpdGUiXX0.PJ1vcPCccfVfrHxCJzRs19xoFV6hnu5lMetJuYARylw",

https://jwt.io/ jwt解析
在這裏插入圖片描述訪問資源成功
在這裏插入圖片描述

jwt token加額外信息

實現TokenEnhancer

在這裏插入圖片描述

WhaleTokenEnhancer

public class WhaleTokenEnhancer implements TokenEnhancer {
    /**
     *
     * @param oAuth2AccessToken
     * @param oAuth2Authentication
     * @return
     */
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {

        Map<String,Object> info = new HashMap<>();
        info.put("company","埃森哲");

        ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);

        return oAuth2AccessToken;
    }
}

TokenStoreConfig 配置 TokenEnhancer bean

·······························
  public static class JwtTokenConfig{

    		·····················
        @Bean
        @ConditionalOnMissingBean(name = "jwtTokenEnhancer")
        public TokenEnhancer jwtTokenEnhancer(){
            return  new WhaleTokenEnhancer();
        }
    }

WhaleAuthenticationServiceConfig 中配置tokenEnhancerChain

部分代碼如下


    @Autowired(required = false)
    private TokenEnhancer jwtTokenEnhancer;

    /**
     *
     * @param endpoints  oauth/token 的入口點
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        super.configure(endpoints);
//        endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
        endpoints.tokenStore(tokenStore)
                 .authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);

        if(jwtAccessTokenConverter!=null && jwtTokenEnhancer!=null){

            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();

            List<TokenEnhancer> enhancers = new ArrayList<>();
            enhancers.add(jwtTokenEnhancer);
            enhancers.add(jwtAccessTokenConverter);

            tokenEnhancerChain.setTokenEnhancers(enhancers);

            endpoints.tokenEnhancer(tokenEnhancerChain)
                     .accessTokenConverter(jwtAccessTokenConverter);
        }
    }

測試

在這裏插入圖片描述eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCIsInJlYWQiLCJ3cml0ZSJdLCJjb21wYW55Ijoi5ZD5qOu5ZOyIiwiZXhwIjoxNTU4NjEzNjQzLCJhdXRob3JpdGllcyI6WyJhZG1pbiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI5OTA3NWZjOC04MTI3LTQ2YWYtYTM2OS0wNTlkODlkNTIwMTEiLCJjbGllbnRfaWQiOiJ3aGFsZSJ9.1Ild0mbgxQn1No0NQIQI2gWrTDFDcgdHYHdC_kdGq_w

解析如下

在這裏插入圖片描述

Authentication解析額外信息?

注意 雖然我們的jwt可以攜帶額外信息
但是 我們的spring 只是 將 jwt規範裏面的信息解析成 Authentication
在這裏插入圖片描述
如果我們需要jwt token 裏的額外信息呢?

爲此我們需要加入新的依賴

https://jwt.io/
https://www.jsonwebtoken.io/

demo pom 文件中加依賴

試了幾個 0.9.0 版本可以

 <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

解析額外信息 UserController

    @GetMapping("/me11")
    public Object getCurrentUser11(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException {
        //spring 會自動找到Authentication類型的數據注入
        String header = request.getHeader("Authorization");
        String token = StringUtils.substringAfter(header, "Bearer ");

        //需要用祕鑰來驗證簽名
        //注意 簽名的時候 默認用的是祕鑰的utf-8編碼 ,同時驗籤的時候也要知道 編碼 utf-8
        Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("utf-8"))
                .parseClaimsJws(token).getBody();

        String company = (String) claims.get("company");

        System.out.println(company);
        return  authentication;
    }

測試

在這裏插入圖片描述
在這裏插入圖片描述

token令牌的刷新

refresh_token

在用戶無感的情況下用refresh_token得到一個新的token
在這裏插入圖片描述

測試

但我的refresh_token沒有測試成功
也不知道什麼原因

在這裏插入圖片描述

按道理是完全可以的,也不知道問題出在哪

https://auth0.com/docs/tokens/refresh-token/current#9e51c5c22d4b450cab209018dc3be588_java

Use a Refresh Token
To exchange the Refresh Token you received during authorization for a new Access Token, make a POST request to the /oauth/token endpoint in the Authentication API, using grant_type=refresh_token.

curl --request POST \
  --url 'https://YOUR_DOMAIN/oauth/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=refresh_token&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&refresh_token=YOUR_REFRESH_TOKEN'

https://openapi.baidu.com/wiki/index.php?title=使用Refresh_Token獲取Access_Token

https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

https://auth0.com/docs/tokens/refresh-token/current

https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#acquiring--client-ids-and-secrets

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章