分類
發燒車訊

OAuth + Security – 3 – JWT令牌_租車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

PS:此文章為系列文章,建議從第一篇開始閱讀。

為什麼使用JWT令牌

在上面的資源服務器中,通過配置,我們了解到,當我們程序是前後端分離時,在拿着token去獲取資源時,程序會先去調用遠程認證服務器的端點去驗證解析token,這樣毫無疑問,當訪問量過大的時候,對認證服務器的壓力可想而知,所以為了解決上面的問題,我們採用JWT令牌格式,可以優化上面的問題。

令牌採用JWT格式即可解決上邊的問題,用戶認證通過會得到一個JWT令牌,JWT令牌中已經包括了用戶相關的信息,客戶端只需要攜帶JWT訪問資源服務,資源服務根據事先約定的算法自行完成令牌校驗,無需每次都請求認證服務完成授權。

改造認證服務器

  1. 修改TokenConfig類,如下:
@Configuration
public class TokenConfigure {

    private static final String SIGNING_KEY = "dimples";

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        //對稱秘鑰,資源服務器使用該秘鑰來驗證
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }


}
  1. 修改認證服務器的配置
private TokenStore tokenStore;

private ClientDetailsService clientDetailsService;

private JwtAccessTokenConverter jwtAccessTokenConverter;
//通過構造方法注入
...


/**
 * 令牌管理服務
 *
 * @return TokenServices
 */
@Bean
public AuthorizationServerTokenServices tokenServices() {
    DefaultTokenServices services = new DefaultTokenServices();
    // 客戶端詳情服務
    services.setClientDetailsService(clientDetailsService);
    // 支持令牌刷新
    services.setSupportRefreshToken(true);
    // 令牌存儲策略
    services.setTokenStore(tokenStore);
    
    // 配置令牌增強 JWT
    TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(jwtAccessTokenConverter));
    services.setTokenEnhancer(tokenEnhancerChain);
    
    // 令牌默認有效期2小時(如果客戶端設置了會覆蓋該值)
    services.setAccessTokenValiditySeconds(7200);
    // 刷新令牌默認有效期2天
    services.setRefreshTokenValiditySeconds(259200);
    return services;
}
  1. 最後別忘了在pom中添加JWT的依賴,否則項目將會報錯
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-jwt</artifactId>
	<version>1.1.0.RELEASE</version>
</dependency>

測試結果如下:

可以使用OAuth的/oauth/check_token端點來解析驗證一下該token

但是我們需要明白一點的是,這種令牌還是存儲在內存中的,後期我們如何將其存儲到redis中是我們研究的方向。

改造資源服務器

當我們使用了JWT令牌以後,由於在JWT令牌中我們存儲了相應的用戶信息和權限,這時我們可以直接在資源服務器中直接去解析對應令牌,就不用每次都去請求認證服務器端點,加大認證服務器的壓力,下面我們開始改造資源服務器:

  1. 將上面認證服務器中寫的TokenConfigure類拷貝一份到資源服務器
  2. 在資源服務器中屏蔽調之前的資源服務器令牌解析服務( tokenService() )
  3. 注入TokenConfigure類,然後配置到ResourceServerSecurityConfigurer里

完整的配置如下:

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class DimplesResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

    public static final String RESOURCE_ID = "dimples";

    private TokenStore tokenStore;
    

    @Autowired
    public DimplesResourceServerConfigurerAdapter(TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID)
                .tokenServices(tokenService())
                .stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // 配置客戶端權限scope
                .antMatchers("/**").access("#oauth2.hasScope('all')")
                .and().csrf().disable()
                // 關閉session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

然後重啟服務,重新獲取令牌,然後訪問之前的測試接口:

待解決的問題

在此處的JWT的配置中,我們獲取的令牌信息還是存在內存中的,這樣不利於我們程序的擴展。那麼我們如何將生產的令牌去存儲到數據庫中或者存儲到redis中呢?請關注後續的文章。

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

使用非對稱加密

在上面的jwt加密中,我們的JWT簽名是寫死的字符串,可能我們的項目為了安全考慮,需要使用非對稱的加密,我們該怎麼配置呢?

首先獲取加密文件的私鑰和公鑰,需要先安裝安裝OpenSSL工具,參考鏈接【https://blog.csdn.net/qq_39081974/article/details/81059022】

  1. 生成JKS Java KeyStore文件

命令行執行:keytool -genkeypair -alias dimples -keyalg RSA -keypass dimples -keystore dimples.jks -storepass dimples

將生成一個名為medical.jks的文件,其中包含我們的密鑰 – 公鑰和私鑰。 還要確保keypass和storepass是一樣的

  1. 導出公鑰

keytool -list -rfc –keystore dimples.jks | openssl x509 -inform pem -pubkey

或 keytool -importkeystore -srckeystore dimples.jks -destkeystore dimples.jks -deststoretype pkcs12

將其複製到我們的資源服務器 src/main/resources/public.txt 中

  1. 配置認證服務器(TokenConfigure)
/**
 * 配置jwt生成token的轉換
 * 使用RSA Sign Key 進行加密
 *
 * @return JwtAccessTokenConverter
 */
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
    KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("medical.jks"), "medical".toCharArray());
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setKeyPair(keyStoreKeyFactory.getKeyPair("medical"));
    return converter;
}
  1. 配置資源服務器(TokenConfigure)
/**
 * 配置jwt生成token的轉換
 *
 * @return JwtAccessTokenConverter
 */
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    Resource resource = new ClassPathResource("public.txt");
    String publicKey;
    try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
        publicKey = bufferedReader.lines().collect(Collectors.joining("\n"));
    } catch (final IOException e) {
        throw new RuntimeException(e);
    }
    converter.setSigningKey(publicKey);
    return converter;
}

擴展JWT的存儲信息

當我們使用如上的配置獲取Token后,將access_token中的內容複製到https://jwt.io/網站解析下:

可以看到在jwt中只是保存了我們的user_name,那麼我們怎麼去擴展呢?讓其可以保存我們需要的用戶詳細信息,這裡有兩種方案:

  • 實現TokenEnhancer(Token增強器)
public class JWTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        Map<String, Object> info = new HashMap<>();
        info.put("other", "hello world");
        ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
        return oAuth2AccessToken;
    }
}

然後在TokenConfigure 中配置該Bean:

@Configuration
public class TokenConfigure {
    ......

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new JWTokenEnhancer();
    }
}

最後在認證服務器里配置該增強器:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private TokenEnhancer tokenEnhancer;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        .....
        
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancers = new ArrayList<>();
        enhancers.add(tokenEnhancer);
        enhancers.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(enhancers);

        endpoints.tokenEnhancer(enhancerChain);
    }
    ......
}
  • 擴展username的內容

比如存入json數據內容作為username的內容。相比較而言,方案二比較簡單還不用破壞UserDetails的結構

@Override 
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    //登錄賬號
    System.out.println("username="+username);
    //根據賬號去數據庫查詢...
    UserDto user = userDao.getUserByUsername(username);
    if(user == null){
        return null;
    }
    //查詢用戶權限
    List<String> permissions = userDao.findPermissionsByUserId(user.getId());
    String[] perarray = new String[permissions.size()];
    permissions.toArray(perarray);
    //創建userDetails
    //這裏將user轉為json,將整體user存入userDetails
    String principal = JSON.toJSONString(user);
    UserDetails userDetails = User.withUsername(principal).password(user.getPassword()).authorities(perarray).build();
    return userDetails;
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。