最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JWT Token實現(xiàn)方法及步驟詳解

 更新時間:2020年09月04日 09:23:19   作者:碼農(nóng)小胖哥  
這篇文章主要介紹了JWT Token實現(xiàn)方法及步驟詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

1. 前言

Json Web Token (JWT) 近幾年是前后端分離常用的 Token 技術(shù),是目前最流行的跨域身份驗證解決方案。你可以通過文章 一文了解web無狀態(tài)會話token技術(shù)JWT 來了解 JWT。今天我們來手寫一個通用的 JWT 服務。DEMO 獲取方式在文末,實現(xiàn)在 jwt 相關包下

2. spring-security-jwt

spring-security-jwt 是 Spring Security Crypto 提供的 JWT 工具包 。

 <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
    <version>${spring-security-jwt.version}</version>
 </dependency>

核心類只有一個: org.springframework.security.jwt.JwtHelper 。它提供了兩個非常有用的靜態(tài)方法。

3. JWT 編碼

JwtHelper 提供的第一個靜態(tài)方法就是 encode(CharSequence content, Signer signer) 這個是用來生成jwt的方法 需要指定 payload 跟 signer 簽名算法。payload 存放了一些可用的不敏感信息:

  • iss jwt簽發(fā)者
  • sub jwt所面向的用戶
  • aud 接收jwt的一方
  • iat jwt的簽發(fā)時間
  • exp jwt的過期時間,這個過期時間必須要大于簽發(fā)時間 iat
  • jti jwt的唯一身份標識,主要用來作為一次性token,從而回避重放***

除了以上提供的基本信息外,我們可以定義一些我們需要傳遞的信息,比如目標用戶的權(quán)限集 等等。切記不要傳遞密碼等敏感信息 ,因為 JWT 的前兩段都是用了 BASE64 編碼,幾乎算是明文了。

3.1 構(gòu)建 JWT 中的 payload

我們先來構(gòu)建 payload :

 /**
 * 構(gòu)建 jwt payload
 *
 * @author Felordcn
 * @since 11:27 2019/10/25
 **/
 public class JwtPayloadBuilder {

   private Map<String, String> payload = new HashMap<>();
   /**
   * 附加的屬性
   */
   private Map<String, String> additional;
   /**
   * jwt簽發(fā)者
   **/
   private String iss;
   /**
   * jwt所面向的用戶
   **/
   private String sub;
   /**
   * 接收jwt的一方
   **/
   private String aud;
   /**
   * jwt的過期時間,這個過期時間必須要大于簽發(fā)時間
   **/
   private LocalDateTime exp;
   /**
   * jwt的簽發(fā)時間
   **/
   private LocalDateTime iat = LocalDateTime.now();
   /**
   * 權(quán)限集
   */
   private Set<String> roles = new HashSet<>();
   /**
   * jwt的唯一身份標識,主要用來作為一次性token,從而回避重放***
   **/
   private String jti = IdUtil.simpleUUID();

   public JwtPayloadBuilder iss(String iss) {
     this.iss = iss;
     return this;
   }

   public JwtPayloadBuilder sub(String sub) {
     this.sub = sub;
     return this;
   }

   public JwtPayloadBuilder aud(String aud) {
     this.aud = aud;
     return this;
   }

   public JwtPayloadBuilder roles(Set<String> roles) {
     this.roles = roles;
     return this;
   }

   public JwtPayloadBuilder expDays(int days) {
     Assert.isTrue(days > 0, "jwt expireDate must after now");
     this.exp = this.iat.plusDays(days);
     return this;
   }

   public JwtPayloadBuilder additional(Map<String, String> additional) {
     this.additional = additional;
     return this;
   }

   public String builder() {
     payload.put("iss", this.iss);
     payload.put("sub", this.sub);
     payload.put("aud", this.aud);
     payload.put("exp", this.exp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
     payload.put("iat", this.iat.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
     payload.put("jti", this.jti);

     if (!CollectionUtils.isEmpty(additional)) {
       payload.putAll(additional);
     }
     payload.put("roles", JSONUtil.toJsonStr(this.roles));
     return JSONUtil.toJsonStr(JSONUtil.parse(payload));

   }

 }

通過建造類 JwtClaimsBuilder 我們可以很方便來構(gòu)建 JWT 所需要的 payload json 字符串傳遞給 encode(CharSequence content, Signer signer) 中的 content 。

3.2 生成 RSA 密鑰并進行簽名

為了生成 JWT Token 我們還需要使用 RSA 算法來進行簽名。這里我們使用 JDK 提供的證書管理工具 Keytool 來生成 RSA 證書 ,格式為 jks 格式。

生成證書命令參考:

```shell script keytool -genkey -alias felordcn -keypass felordcn -keyalg RSA -storetype PKCS12 -keysize 1024 -validity 365 -keystore d:/keystores/felordcn.jks -storepass 123456 -dname "CN=(Felord), OU=(felordcn), O=(felordcn), L=(zz), ST=(hn), C=(cn)"

其中 `-alias felordcn -storepass 123456` 我們要作為配置使用要記下來。我們要使用下面定義的這個類來讀取證書

```java
 package cn.felord.spring.security.jwt;

 import org.springframework.core.io.ClassPathResource;

 import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyStore;
 import java.security.PublicKey;
 import java.security.interfaces.RSAPrivateCrtKey;
 import java.security.spec.RSAPublicKeySpec;

 /**
 * KeyPairFactory
 *
 * @author Felordcn
 * @since 13:41 2019/10/25
 **/
 class KeyPairFactory {

   private KeyStore store;

   private final Object lock = new Object();

   /**
   * 獲取公私鑰.
   *
   * @param keyPath jks 文件在 resources 下的classpath
   * @param keyAlias keytool 生成的 -alias 值 felordcn
   * @param keyPass keytool 生成的 -keypass 值 felordcn
   * @return the key pair 公私鑰對
   */
  KeyPair create(String keyPath, String keyAlias, String keyPass) {
     ClassPathResource resource = new ClassPathResource(keyPath);
     char[] pem = keyPass.toCharArray();
     try {
       synchronized (lock) {
         if (store == null) {
           synchronized (lock) {
             store = KeyStore.getInstance("jks");
             store.load(resource.getInputStream(), pem);
           }
         }
       }
       RSAPrivateCrtKey key = (RSAPrivateCrtKey) store.getKey(keyAlias, pem);
       RSAPublicKeySpec spec = new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent());
       PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(spec);
       return new KeyPair(publicKey, key);
     } catch (Exception e) {
       throw new IllegalStateException("Cannot load keys from store: " + resource, e);
     }

   }
 }

獲取了 KeyPair 就能獲取公私鑰 生成 Jwt 的兩個要素就完成了。我們可以和之前定義的 JwtPayloadBuilder 一起封裝出生成 Jwt Token 的方法:

   private String jwtToken(String aud, int exp, Set<String> roles, Map<String, String> additional) {
     String payload = jwtPayloadBuilder
         .iss(jwtProperties.getIss())
         .sub(jwtProperties.getSub())
         .aud(aud)
         .additional(additional)
         .roles(roles)
         .expDays(exp)
         .builder();
     RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

     RsaSigner signer = new RsaSigner(privateKey);
     return JwtHelper.encode(payload, signer).getEncoded();
   }

通常情況下 Jwt Token 都是成對出現(xiàn)的,一個為平常請求攜帶的 accessToken, 另一個只作為刷新 accessToken 之用的 refreshToken 。而且 refreshToken 的過期時間要相對長一些。當 accessToken 失效而refreshToken 有效時,我們可以通過 refreshToken 來獲取新的 Jwt Token對 ;當兩個都失效就用戶就必須重新登錄了。

生成 Jwt Token對 的方法如下:

   public JwtTokenPair jwtTokenPair(String aud, Set<String> roles, Map<String, String> additional) {
     String accessToken = jwtToken(aud, jwtProperties.getAccessExpDays(), roles, additional);
     String refreshToken = jwtToken(aud, jwtProperties.getRefreshExpDays(), roles, additional);

     JwtTokenPair jwtTokenPair = new JwtTokenPair();
     jwtTokenPair.setAccessToken(accessToken);
     jwtTokenPair.setRefreshToken(refreshToken);
     // 放入緩存
     jwtTokenStorage.put(jwtTokenPair, aud);
     return jwtTokenPair;
   }

通常 Jwt Token對 會在返回給前臺的同時放入緩存中。過期策略你可以選擇分開處理,也可以選擇以refreshToken 的過期時間為準。

4. JWT 解碼以及驗證

JwtHelper 提供的第二個靜態(tài)方法是Jwt decodeAndVerify(String token, SignatureVerifier verifier) 用來 驗證和解碼 Jwt Token 。我們獲取到請求中的token后會解析出用戶的一些信息。通過這些信息去緩存中對應的token ,然后比對并驗證是否有效(包括是否過期)。

   /**
    * 解碼 并校驗簽名 過期不予解析
    *
    * @param jwtToken the jwt token
    * @return the jwt claims
    */
   public JSONObject decodeAndVerify(String jwtToken) {
     Assert.hasText(jwtToken, "jwt token must not be bank");
     RSAPublicKey rsaPublicKey = (RSAPublicKey) this.keyPair.getPublic();
     SignatureVerifier rsaVerifier = new RsaVerifier(rsaPublicKey);
     Jwt jwt = JwtHelper.decodeAndVerify(jwtToken, rsaVerifier);
     String claims = jwt.getClaims();
     JSONObject jsonObject = JSONUtil.parseObj(claims);
     String exp = jsonObject.getStr(JWT_EXP_KEY);
     // 是否過期
     if (isExpired(exp)) {
       throw new IllegalStateException("jwt token is expired");
     }
     return jsonObject;
   }

上面我們將有效的 Jwt Token 中的 payload 解析為 JSON對象 ,方便后續(xù)的操作。

5. 配置

我們將 JWT 的可配置項抽出來放入 JwtProperties 如下:

 /**
 * Jwt 在 springboot application.yml 中的配置文件
 *
 * @author Felordcn
 * @since 15 :06 2019/10/25
 */
 @Data
 @ConfigurationProperties(prefix=JWT_PREFIX)
 public class JwtProperties {
   static final String JWT_PREFIX= "jwt.config";
   /**
   * 是否可用
   */
   private boolean enabled;
   /**
   * jks 路徑
   */
   private String keyLocation;
   /**
   * key alias
   */
   private String keyAlias;
   /**
   * key store pass
   */
   private String keyPass;
   /**
   * jwt簽發(fā)者
   **/
   private String iss;
   /**
   * jwt所面向的用戶
   **/
   private String sub;
   /**
   * access jwt token 有效天數(shù)
   */
   private int accessExpDays;
   /**
   * refresh jwt token 有效天數(shù)
   */
   private int refreshExpDays;
 }

然后我們就可以配置 JWT 的 javaConfig 如下:

 /**
 * JwtConfiguration
 *
 * @author Felordcn
 * @since 16 :54 2019/10/25
 */
 @EnableConfigurationProperties(JwtProperties.class)
 @ConditionalOnProperty(prefix = "jwt.config",name = "enabled")
 @Configuration
 public class JwtConfiguration {

   /**
   * Jwt token storage .
   *
   * @return the jwt token storage
   */
   @Bean
   public JwtTokenStorage jwtTokenStorage() {
     return new JwtTokenCacheStorage();
   }

   /**
   * Jwt token generator.
   *
   * @param jwtTokenStorage the jwt token storage
   * @param jwtProperties  the jwt properties
   * @return the jwt token generator
   */
   @Bean
   public JwtTokenGenerator jwtTokenGenerator(JwtTokenStorage jwtTokenStorage, JwtProperties jwtProperties) {
     return new JwtTokenGenerator(jwtTokenStorage, jwtProperties);
   }

 }

然后你就可以通過 JwtTokenGenerator 編碼/解碼驗證 Jwt Token 對 ,通過 JwtTokenStorage 來處理 Jwt Token 緩存。緩存這里我用了Spring Cache Ehcache 來實現(xiàn),你也可以切換到 Redis 。相關單元測試參見 DEMO

6. 總結(jié)

今天我們利用 spring-security-jwt 手寫了一套 JWT 邏輯。無論對你后續(xù)結(jié)合 Spring Security 還是 Shiro 都十分有借鑒意義。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • Java高并發(fā)中的交換器Exchanger解析

    Java高并發(fā)中的交換器Exchanger解析

    這篇文章主要介紹了Java高并發(fā)中的交換器Exchanger解析,如果兩個線程并行處理,但在某個時刻需要互相交換自己已經(jīng)處理完的中間數(shù)據(jù),然后才能繼續(xù)往下執(zhí)行,這個時候就可以使用 Exchanger,需要的朋友可以參考下
    2023-12-12
  • java中jdbcTemplate的queryForList(坑)

    java中jdbcTemplate的queryForList(坑)

    本文主要介紹了java中jdbcTemplate的queryForList,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • mvn compile報錯“程序包com.XXX不存在”

    mvn compile報錯“程序包com.XXX不存在”

    本文主要介紹了mvn compile報錯“程序包com.XXX不存在”,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • Springboot 讀取自定義pro文件注入static靜態(tài)變量方式

    Springboot 讀取自定義pro文件注入static靜態(tài)變量方式

    這篇文章主要介紹了Springboot 讀取自定義pro文件注入static靜態(tài)變量方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • mybatis-plus配置控制臺打印完整帶參數(shù)SQL語句的實現(xiàn)

    mybatis-plus配置控制臺打印完整帶參數(shù)SQL語句的實現(xiàn)

    這篇文章主要介紹了mybatis-plus配置控制臺打印完整帶參數(shù)SQL語句,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • Java連接Sql數(shù)據(jù)庫經(jīng)常用到的操作

    Java連接Sql數(shù)據(jù)庫經(jīng)常用到的操作

    這篇文章主要介紹了Java連接Sql數(shù)據(jù)庫經(jīng)常用到的操作的相關資料,需要的朋友可以參考下
    2016-02-02
  • MybatisPlus中selectPage的使用方法

    MybatisPlus中selectPage的使用方法

    本文主要介紹了MybatisPlus中selectPage的使用方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-05-05
  • java實現(xiàn)統(tǒng)一異常處理的示例

    java實現(xiàn)統(tǒng)一異常處理的示例

    一個全局異常處理類需要處理三類異常1.業(yè)務類異常,2.運行時異常 ,3.Error,本文給大家介紹java實現(xiàn)統(tǒng)一異常處理的示例,感興趣的朋友一起看看吧
    2021-06-06
  • Java 添加、刪除、格式化Word中的圖片步驟詳解( 基于Spire.Cloud.SDK for Java )

    Java 添加、刪除、格式化Word中的圖片步驟詳解( 基于Spire.Cloud.SDK for Java )

    這篇文章主要介紹了Java 添加、刪除、格式化Word中的圖片( 基于Spire.Cloud.SDK for Java ),本文分步驟通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • Java多線程的臨界資源問題解決方案

    Java多線程的臨界資源問題解決方案

    這篇文章主要介紹了Java多線程的臨界資源問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02

最新評論

新源县| 云南省| 江门市| 定陶县| 新野县| 牟定县| 绥滨县| 天等县| 根河市| 股票| 武山县| 东平县| 平陆县| 富民县| 静安区| 甘肃省| 桃江县| 盱眙县| 石门县| 绥中县| 慈溪市| 左权县| 安平县| 建昌县| 延吉市| 井陉县| 鹤壁市| 扶沟县| 永登县| 会昌县| 桃江县| 二连浩特市| 新昌县| 年辖:市辖区| 海丰县| 鹤峰县| 班玛县| 奎屯市| 保德县| 景东| 偏关县|