'JWT+Interceptor實現無狀態登錄和鑑權'

"

無狀態登錄原理#

先提一下啥是有狀態登錄#

單臺tomcat的情況下:編碼的流程如下

  1. 前端提交表單裡用戶的輸入的賬號密碼
  2. 後臺接受,查數據庫,
  3. 在數據庫中找到用戶的信息後,把用戶的信息存放到session裡面,返回給用戶cookie
  4. 以後用戶的請求都會自動攜帶著cookie去訪問後臺,後臺根據用戶的cookie辨識用戶的身份

但是有缺點

  • 如果有千千萬的用戶都往session裡面存放信息,
  • session很難跨服務器,也就是說,用戶每次請求,都必須訪問同一臺tomcat,新的tomcat不認識用戶的cookie

無狀態登錄#

  • 對於服務端,不再保存任何用戶的信息,對於他們來說,所有的用戶地位平等
  • 對於用戶,他們需要自己攜帶著信息去訪問服務端,攜帶的信息可以被所有服務端辨認,所以,對於用戶,所有的服務地位平等

具體如何實現呢?

  • 用戶登錄,服務端返回給用戶的個人信息+token令牌
  • 前端為了自己使用方便,將用戶的個人信息緩存進瀏覽器(因為後端只是給了他一個token)
  • 用戶攜帶自己的token去訪問服務端
  • 認證通過,把用戶請求的數據返回給客戶端
  • 以後不論用戶請求哪個微服務,都攜帶著token
  • 微服務對token進行解密,判斷他是否有效
"

無狀態登錄原理#

先提一下啥是有狀態登錄#

單臺tomcat的情況下:編碼的流程如下

  1. 前端提交表單裡用戶的輸入的賬號密碼
  2. 後臺接受,查數據庫,
  3. 在數據庫中找到用戶的信息後,把用戶的信息存放到session裡面,返回給用戶cookie
  4. 以後用戶的請求都會自動攜帶著cookie去訪問後臺,後臺根據用戶的cookie辨識用戶的身份

但是有缺點

  • 如果有千千萬的用戶都往session裡面存放信息,
  • session很難跨服務器,也就是說,用戶每次請求,都必須訪問同一臺tomcat,新的tomcat不認識用戶的cookie

無狀態登錄#

  • 對於服務端,不再保存任何用戶的信息,對於他們來說,所有的用戶地位平等
  • 對於用戶,他們需要自己攜帶著信息去訪問服務端,攜帶的信息可以被所有服務端辨認,所以,對於用戶,所有的服務地位平等

具體如何實現呢?

  • 用戶登錄,服務端返回給用戶的個人信息+token令牌
  • 前端為了自己使用方便,將用戶的個人信息緩存進瀏覽器(因為後端只是給了他一個token)
  • 用戶攜帶自己的token去訪問服務端
  • 認證通過,把用戶請求的數據返回給客戶端
  • 以後不論用戶請求哪個微服務,都攜帶著token
  • 微服務對token進行解密,判斷他是否有效
JWT+Interceptor實現無狀態登錄和鑑權


JWT(Json Web Token)生成規則#

整個登錄.授權.鑑權的過程token的安全性至關重要,而JWT就是一門有關於如何生成一個不可仿造的token的規範

他是JSON風格輕量級的授權和身份認證規範,可實現無狀態、分佈式的Web應用授權,而且它不是技術,和語言無關,java有對這個規範的實現 叫做 jjwt -- 點擊進入jjwt的github項目

"

無狀態登錄原理#

先提一下啥是有狀態登錄#

單臺tomcat的情況下:編碼的流程如下

  1. 前端提交表單裡用戶的輸入的賬號密碼
  2. 後臺接受,查數據庫,
  3. 在數據庫中找到用戶的信息後,把用戶的信息存放到session裡面,返回給用戶cookie
  4. 以後用戶的請求都會自動攜帶著cookie去訪問後臺,後臺根據用戶的cookie辨識用戶的身份

但是有缺點

  • 如果有千千萬的用戶都往session裡面存放信息,
  • session很難跨服務器,也就是說,用戶每次請求,都必須訪問同一臺tomcat,新的tomcat不認識用戶的cookie

無狀態登錄#

  • 對於服務端,不再保存任何用戶的信息,對於他們來說,所有的用戶地位平等
  • 對於用戶,他們需要自己攜帶著信息去訪問服務端,攜帶的信息可以被所有服務端辨認,所以,對於用戶,所有的服務地位平等

具體如何實現呢?

  • 用戶登錄,服務端返回給用戶的個人信息+token令牌
  • 前端為了自己使用方便,將用戶的個人信息緩存進瀏覽器(因為後端只是給了他一個token)
  • 用戶攜帶自己的token去訪問服務端
  • 認證通過,把用戶請求的數據返回給客戶端
  • 以後不論用戶請求哪個微服務,都攜帶著token
  • 微服務對token進行解密,判斷他是否有效
JWT+Interceptor實現無狀態登錄和鑑權


JWT(Json Web Token)生成規則#

整個登錄.授權.鑑權的過程token的安全性至關重要,而JWT就是一門有關於如何生成一個不可仿造的token的規範

他是JSON風格輕量級的授權和身份認證規範,可實現無狀態、分佈式的Web應用授權,而且它不是技術,和語言無關,java有對這個規範的實現 叫做 jjwt -- 點擊進入jjwt的github項目

JWT+Interceptor實現無狀態登錄和鑑權

由JWT算法得到的token格式如上圖,由頭部,載荷,簽名三部分組成

  • 頭部
  • 由兩部分組成,alg表示使用的加密算法 typ表示使用的token的類型
  • 載荷,存放用戶相關的信息
Copy
// 下面是它已經定義好的載荷部分key,也允許我們自定義載荷部分key
iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什麼時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
  • 第三步分的簽名是由 頭+載荷+鹽 三部分加密組成

從圖可以看出,頭部和載荷被串改後,生成的編碼會發生改變,因此保證了token的安全 ,還有,載荷部分可解密,因此不要往裡面放入敏感的信息(比如密碼 )

只要密鑰不洩露,別人就無法偽造任何信息

jwt的交互過程

"

無狀態登錄原理#

先提一下啥是有狀態登錄#

單臺tomcat的情況下:編碼的流程如下

  1. 前端提交表單裡用戶的輸入的賬號密碼
  2. 後臺接受,查數據庫,
  3. 在數據庫中找到用戶的信息後,把用戶的信息存放到session裡面,返回給用戶cookie
  4. 以後用戶的請求都會自動攜帶著cookie去訪問後臺,後臺根據用戶的cookie辨識用戶的身份

但是有缺點

  • 如果有千千萬的用戶都往session裡面存放信息,
  • session很難跨服務器,也就是說,用戶每次請求,都必須訪問同一臺tomcat,新的tomcat不認識用戶的cookie

無狀態登錄#

  • 對於服務端,不再保存任何用戶的信息,對於他們來說,所有的用戶地位平等
  • 對於用戶,他們需要自己攜帶著信息去訪問服務端,攜帶的信息可以被所有服務端辨認,所以,對於用戶,所有的服務地位平等

具體如何實現呢?

  • 用戶登錄,服務端返回給用戶的個人信息+token令牌
  • 前端為了自己使用方便,將用戶的個人信息緩存進瀏覽器(因為後端只是給了他一個token)
  • 用戶攜帶自己的token去訪問服務端
  • 認證通過,把用戶請求的數據返回給客戶端
  • 以後不論用戶請求哪個微服務,都攜帶著token
  • 微服務對token進行解密,判斷他是否有效
JWT+Interceptor實現無狀態登錄和鑑權


JWT(Json Web Token)生成規則#

整個登錄.授權.鑑權的過程token的安全性至關重要,而JWT就是一門有關於如何生成一個不可仿造的token的規範

他是JSON風格輕量級的授權和身份認證規範,可實現無狀態、分佈式的Web應用授權,而且它不是技術,和語言無關,java有對這個規範的實現 叫做 jjwt -- 點擊進入jjwt的github項目

JWT+Interceptor實現無狀態登錄和鑑權

由JWT算法得到的token格式如上圖,由頭部,載荷,簽名三部分組成

  • 頭部
  • 由兩部分組成,alg表示使用的加密算法 typ表示使用的token的類型
  • 載荷,存放用戶相關的信息
Copy
// 下面是它已經定義好的載荷部分key,也允許我們自定義載荷部分key
iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什麼時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
  • 第三步分的簽名是由 頭+載荷+鹽 三部分加密組成

從圖可以看出,頭部和載荷被串改後,生成的編碼會發生改變,因此保證了token的安全 ,還有,載荷部分可解密,因此不要往裡面放入敏感的信息(比如密碼 )

只要密鑰不洩露,別人就無法偽造任何信息

jwt的交互過程

JWT+Interceptor實現無狀態登錄和鑑權

RSA非對稱加密算算法#

由美國麻 省理工 學院三 位學者 Riv est、Sh amir 及Adleman 研 究發 展出 一套 可實 際使用 的公 開金 鑰密 碼系 統,那 就是

RSA(Rivest-Shamir-Adleman)密碼系統

jwt(是一種非對稱加密算法) JWT不一定非要搭配RSA算法,但是擁有RSA算法公鑰私鑰的特性,會使我們的業務邏輯變的簡單,做到校驗變少

  • 對稱加密,如AES(Advanced Encryption Standard) 高級加密標準
  • 基本原理:將明文分成N個組,然後使用密鑰對各個組進行加密,形成各自的密文,最後把所有的分組密文進行合併,形成最終的密文。
  • 優勢:算法公開、計算量小、加密速度快、加密效率高
  • 缺陷:雙方都使用同樣密鑰,安全性得不到保證
  • 非對稱加密,如RSA
  • 基本原理:同時生成兩把密鑰:私鑰和公鑰,私鑰隱祕保存,公鑰可以下發給信任客戶端
  • 私鑰加密,持有私鑰或公鑰才可以解密
  • 公鑰加密,持有私鑰才可解密
  • 優點:安全,難以破解
  • 缺點:算法比較耗時
  • 不可逆加密,如MD5,SHA

使用JJWT實現JWT#

JJWT(java json web token)

座標

Copy
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

創建jwt令牌#

token最終會頒發給前端,這時候就得去和前端的哥們商量它想要什麼信息

Copy
// 生成令牌,主要是用它生成載荷
JwtBuilder builder = Jwts.builder()
// 設置頭部,使用hs256加密, + key,也就是鹽
.signWith(SignatureAlgorithm.HS256,"changwu")
// 添加載荷
.setId("666") // 用戶id
.setSubject("張三") // 用戶名
.setExpiration(new Date(new Date().getTime()+60*1000)) // 過期時間
.setIssuedAt(new Date())// 登錄時間
// 添加自定義的鍵值對
.claim("role","admin");
System.out.println(builder.compact());

經過它處理的token長這個樣子, 三部組成

Copy
XXX.YYY.ZZZ

解析token#

能成功解析出結果的前提是兩次的鹽是一樣的才行

Copy
Claims map = Jwts.parser().setSigningKey("changwu")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjYiLCJzdWIiOiLlvKDkuIkiLCJleHAiOjE1NjU2MTg1MjUsImlhdCI6MTU2NTYxODQ2NSwicm9sZSI6ImFkbWluIn0.GDVfLq-ehSnMCRoxVcziXkirjOg34SUUPBK5vAEHu80")
.getBody();
System.out.println("用戶id" + map.getId());
System.out.println("用戶名" + map.getSubject());
System.out.println("token過期時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getExpiration()));
System.out.println("用戶登錄時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getIssuedAt()));
System.out.println("用戶的角色是:"+map.get("role"));

攔截器#

注意哦,使用的是SpringMvc的攔截器,而不是Servlet的過濾器

攔截器的體系架構#

"

無狀態登錄原理#

先提一下啥是有狀態登錄#

單臺tomcat的情況下:編碼的流程如下

  1. 前端提交表單裡用戶的輸入的賬號密碼
  2. 後臺接受,查數據庫,
  3. 在數據庫中找到用戶的信息後,把用戶的信息存放到session裡面,返回給用戶cookie
  4. 以後用戶的請求都會自動攜帶著cookie去訪問後臺,後臺根據用戶的cookie辨識用戶的身份

但是有缺點

  • 如果有千千萬的用戶都往session裡面存放信息,
  • session很難跨服務器,也就是說,用戶每次請求,都必須訪問同一臺tomcat,新的tomcat不認識用戶的cookie

無狀態登錄#

  • 對於服務端,不再保存任何用戶的信息,對於他們來說,所有的用戶地位平等
  • 對於用戶,他們需要自己攜帶著信息去訪問服務端,攜帶的信息可以被所有服務端辨認,所以,對於用戶,所有的服務地位平等

具體如何實現呢?

  • 用戶登錄,服務端返回給用戶的個人信息+token令牌
  • 前端為了自己使用方便,將用戶的個人信息緩存進瀏覽器(因為後端只是給了他一個token)
  • 用戶攜帶自己的token去訪問服務端
  • 認證通過,把用戶請求的數據返回給客戶端
  • 以後不論用戶請求哪個微服務,都攜帶著token
  • 微服務對token進行解密,判斷他是否有效
JWT+Interceptor實現無狀態登錄和鑑權


JWT(Json Web Token)生成規則#

整個登錄.授權.鑑權的過程token的安全性至關重要,而JWT就是一門有關於如何生成一個不可仿造的token的規範

他是JSON風格輕量級的授權和身份認證規範,可實現無狀態、分佈式的Web應用授權,而且它不是技術,和語言無關,java有對這個規範的實現 叫做 jjwt -- 點擊進入jjwt的github項目

JWT+Interceptor實現無狀態登錄和鑑權

由JWT算法得到的token格式如上圖,由頭部,載荷,簽名三部分組成

  • 頭部
  • 由兩部分組成,alg表示使用的加密算法 typ表示使用的token的類型
  • 載荷,存放用戶相關的信息
Copy
// 下面是它已經定義好的載荷部分key,也允許我們自定義載荷部分key
iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什麼時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
  • 第三步分的簽名是由 頭+載荷+鹽 三部分加密組成

從圖可以看出,頭部和載荷被串改後,生成的編碼會發生改變,因此保證了token的安全 ,還有,載荷部分可解密,因此不要往裡面放入敏感的信息(比如密碼 )

只要密鑰不洩露,別人就無法偽造任何信息

jwt的交互過程

JWT+Interceptor實現無狀態登錄和鑑權

RSA非對稱加密算算法#

由美國麻 省理工 學院三 位學者 Riv est、Sh amir 及Adleman 研 究發 展出 一套 可實 際使用 的公 開金 鑰密 碼系 統,那 就是

RSA(Rivest-Shamir-Adleman)密碼系統

jwt(是一種非對稱加密算法) JWT不一定非要搭配RSA算法,但是擁有RSA算法公鑰私鑰的特性,會使我們的業務邏輯變的簡單,做到校驗變少

  • 對稱加密,如AES(Advanced Encryption Standard) 高級加密標準
  • 基本原理:將明文分成N個組,然後使用密鑰對各個組進行加密,形成各自的密文,最後把所有的分組密文進行合併,形成最終的密文。
  • 優勢:算法公開、計算量小、加密速度快、加密效率高
  • 缺陷:雙方都使用同樣密鑰,安全性得不到保證
  • 非對稱加密,如RSA
  • 基本原理:同時生成兩把密鑰:私鑰和公鑰,私鑰隱祕保存,公鑰可以下發給信任客戶端
  • 私鑰加密,持有私鑰或公鑰才可以解密
  • 公鑰加密,持有私鑰才可解密
  • 優點:安全,難以破解
  • 缺點:算法比較耗時
  • 不可逆加密,如MD5,SHA

使用JJWT實現JWT#

JJWT(java json web token)

座標

Copy
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

創建jwt令牌#

token最終會頒發給前端,這時候就得去和前端的哥們商量它想要什麼信息

Copy
// 生成令牌,主要是用它生成載荷
JwtBuilder builder = Jwts.builder()
// 設置頭部,使用hs256加密, + key,也就是鹽
.signWith(SignatureAlgorithm.HS256,"changwu")
// 添加載荷
.setId("666") // 用戶id
.setSubject("張三") // 用戶名
.setExpiration(new Date(new Date().getTime()+60*1000)) // 過期時間
.setIssuedAt(new Date())// 登錄時間
// 添加自定義的鍵值對
.claim("role","admin");
System.out.println(builder.compact());

經過它處理的token長這個樣子, 三部組成

Copy
XXX.YYY.ZZZ

解析token#

能成功解析出結果的前提是兩次的鹽是一樣的才行

Copy
Claims map = Jwts.parser().setSigningKey("changwu")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjYiLCJzdWIiOiLlvKDkuIkiLCJleHAiOjE1NjU2MTg1MjUsImlhdCI6MTU2NTYxODQ2NSwicm9sZSI6ImFkbWluIn0.GDVfLq-ehSnMCRoxVcziXkirjOg34SUUPBK5vAEHu80")
.getBody();
System.out.println("用戶id" + map.getId());
System.out.println("用戶名" + map.getSubject());
System.out.println("token過期時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getExpiration()));
System.out.println("用戶登錄時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getIssuedAt()));
System.out.println("用戶的角色是:"+map.get("role"));

攔截器#

注意哦,使用的是SpringMvc的攔截器,而不是Servlet的過濾器

攔截器的體系架構#

JWT+Interceptor實現無狀態登錄和鑑權

在攔截器的體系中,我們常用的是上面的來兩個

HandlerInterceptor: 是頂級接口如下:

"

無狀態登錄原理#

先提一下啥是有狀態登錄#

單臺tomcat的情況下:編碼的流程如下

  1. 前端提交表單裡用戶的輸入的賬號密碼
  2. 後臺接受,查數據庫,
  3. 在數據庫中找到用戶的信息後,把用戶的信息存放到session裡面,返回給用戶cookie
  4. 以後用戶的請求都會自動攜帶著cookie去訪問後臺,後臺根據用戶的cookie辨識用戶的身份

但是有缺點

  • 如果有千千萬的用戶都往session裡面存放信息,
  • session很難跨服務器,也就是說,用戶每次請求,都必須訪問同一臺tomcat,新的tomcat不認識用戶的cookie

無狀態登錄#

  • 對於服務端,不再保存任何用戶的信息,對於他們來說,所有的用戶地位平等
  • 對於用戶,他們需要自己攜帶著信息去訪問服務端,攜帶的信息可以被所有服務端辨認,所以,對於用戶,所有的服務地位平等

具體如何實現呢?

  • 用戶登錄,服務端返回給用戶的個人信息+token令牌
  • 前端為了自己使用方便,將用戶的個人信息緩存進瀏覽器(因為後端只是給了他一個token)
  • 用戶攜帶自己的token去訪問服務端
  • 認證通過,把用戶請求的數據返回給客戶端
  • 以後不論用戶請求哪個微服務,都攜帶著token
  • 微服務對token進行解密,判斷他是否有效
JWT+Interceptor實現無狀態登錄和鑑權


JWT(Json Web Token)生成規則#

整個登錄.授權.鑑權的過程token的安全性至關重要,而JWT就是一門有關於如何生成一個不可仿造的token的規範

他是JSON風格輕量級的授權和身份認證規範,可實現無狀態、分佈式的Web應用授權,而且它不是技術,和語言無關,java有對這個規範的實現 叫做 jjwt -- 點擊進入jjwt的github項目

JWT+Interceptor實現無狀態登錄和鑑權

由JWT算法得到的token格式如上圖,由頭部,載荷,簽名三部分組成

  • 頭部
  • 由兩部分組成,alg表示使用的加密算法 typ表示使用的token的類型
  • 載荷,存放用戶相關的信息
Copy
// 下面是它已經定義好的載荷部分key,也允許我們自定義載荷部分key
iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什麼時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
  • 第三步分的簽名是由 頭+載荷+鹽 三部分加密組成

從圖可以看出,頭部和載荷被串改後,生成的編碼會發生改變,因此保證了token的安全 ,還有,載荷部分可解密,因此不要往裡面放入敏感的信息(比如密碼 )

只要密鑰不洩露,別人就無法偽造任何信息

jwt的交互過程

JWT+Interceptor實現無狀態登錄和鑑權

RSA非對稱加密算算法#

由美國麻 省理工 學院三 位學者 Riv est、Sh amir 及Adleman 研 究發 展出 一套 可實 際使用 的公 開金 鑰密 碼系 統,那 就是

RSA(Rivest-Shamir-Adleman)密碼系統

jwt(是一種非對稱加密算法) JWT不一定非要搭配RSA算法,但是擁有RSA算法公鑰私鑰的特性,會使我們的業務邏輯變的簡單,做到校驗變少

  • 對稱加密,如AES(Advanced Encryption Standard) 高級加密標準
  • 基本原理:將明文分成N個組,然後使用密鑰對各個組進行加密,形成各自的密文,最後把所有的分組密文進行合併,形成最終的密文。
  • 優勢:算法公開、計算量小、加密速度快、加密效率高
  • 缺陷:雙方都使用同樣密鑰,安全性得不到保證
  • 非對稱加密,如RSA
  • 基本原理:同時生成兩把密鑰:私鑰和公鑰,私鑰隱祕保存,公鑰可以下發給信任客戶端
  • 私鑰加密,持有私鑰或公鑰才可以解密
  • 公鑰加密,持有私鑰才可解密
  • 優點:安全,難以破解
  • 缺點:算法比較耗時
  • 不可逆加密,如MD5,SHA

使用JJWT實現JWT#

JJWT(java json web token)

座標

Copy
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

創建jwt令牌#

token最終會頒發給前端,這時候就得去和前端的哥們商量它想要什麼信息

Copy
// 生成令牌,主要是用它生成載荷
JwtBuilder builder = Jwts.builder()
// 設置頭部,使用hs256加密, + key,也就是鹽
.signWith(SignatureAlgorithm.HS256,"changwu")
// 添加載荷
.setId("666") // 用戶id
.setSubject("張三") // 用戶名
.setExpiration(new Date(new Date().getTime()+60*1000)) // 過期時間
.setIssuedAt(new Date())// 登錄時間
// 添加自定義的鍵值對
.claim("role","admin");
System.out.println(builder.compact());

經過它處理的token長這個樣子, 三部組成

Copy
XXX.YYY.ZZZ

解析token#

能成功解析出結果的前提是兩次的鹽是一樣的才行

Copy
Claims map = Jwts.parser().setSigningKey("changwu")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjYiLCJzdWIiOiLlvKDkuIkiLCJleHAiOjE1NjU2MTg1MjUsImlhdCI6MTU2NTYxODQ2NSwicm9sZSI6ImFkbWluIn0.GDVfLq-ehSnMCRoxVcziXkirjOg34SUUPBK5vAEHu80")
.getBody();
System.out.println("用戶id" + map.getId());
System.out.println("用戶名" + map.getSubject());
System.out.println("token過期時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getExpiration()));
System.out.println("用戶登錄時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getIssuedAt()));
System.out.println("用戶的角色是:"+map.get("role"));

攔截器#

注意哦,使用的是SpringMvc的攔截器,而不是Servlet的過濾器

攔截器的體系架構#

JWT+Interceptor實現無狀態登錄和鑑權

在攔截器的體系中,我們常用的是上面的來兩個

HandlerInterceptor: 是頂級接口如下:

JWT+Interceptor實現無狀態登錄和鑑權

雖然是接口, 但是擁有jdk8的特性,是默認的方法,所以允許我們挑選它的部分方法實現而不會報錯

prehandler: 請求到達控制器之間被回調,可以在這裡進行設置編碼,安全控制,權限校驗, 一般全部返回ture,表示放行

postHandler: 控制器處理請求之後生成了ModelAndView,但是未進行渲染,提供了修改ModelAndView的機會

afterCompletion: 返回給用戶ModelAndView之後執行, 用於收尾工作

第二個是HandlerInterceptorAdapter如下圖

"

無狀態登錄原理#

先提一下啥是有狀態登錄#

單臺tomcat的情況下:編碼的流程如下

  1. 前端提交表單裡用戶的輸入的賬號密碼
  2. 後臺接受,查數據庫,
  3. 在數據庫中找到用戶的信息後,把用戶的信息存放到session裡面,返回給用戶cookie
  4. 以後用戶的請求都會自動攜帶著cookie去訪問後臺,後臺根據用戶的cookie辨識用戶的身份

但是有缺點

  • 如果有千千萬的用戶都往session裡面存放信息,
  • session很難跨服務器,也就是說,用戶每次請求,都必須訪問同一臺tomcat,新的tomcat不認識用戶的cookie

無狀態登錄#

  • 對於服務端,不再保存任何用戶的信息,對於他們來說,所有的用戶地位平等
  • 對於用戶,他們需要自己攜帶著信息去訪問服務端,攜帶的信息可以被所有服務端辨認,所以,對於用戶,所有的服務地位平等

具體如何實現呢?

  • 用戶登錄,服務端返回給用戶的個人信息+token令牌
  • 前端為了自己使用方便,將用戶的個人信息緩存進瀏覽器(因為後端只是給了他一個token)
  • 用戶攜帶自己的token去訪問服務端
  • 認證通過,把用戶請求的數據返回給客戶端
  • 以後不論用戶請求哪個微服務,都攜帶著token
  • 微服務對token進行解密,判斷他是否有效
JWT+Interceptor實現無狀態登錄和鑑權


JWT(Json Web Token)生成規則#

整個登錄.授權.鑑權的過程token的安全性至關重要,而JWT就是一門有關於如何生成一個不可仿造的token的規範

他是JSON風格輕量級的授權和身份認證規範,可實現無狀態、分佈式的Web應用授權,而且它不是技術,和語言無關,java有對這個規範的實現 叫做 jjwt -- 點擊進入jjwt的github項目

JWT+Interceptor實現無狀態登錄和鑑權

由JWT算法得到的token格式如上圖,由頭部,載荷,簽名三部分組成

  • 頭部
  • 由兩部分組成,alg表示使用的加密算法 typ表示使用的token的類型
  • 載荷,存放用戶相關的信息
Copy
// 下面是它已經定義好的載荷部分key,也允許我們自定義載荷部分key
iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什麼時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
  • 第三步分的簽名是由 頭+載荷+鹽 三部分加密組成

從圖可以看出,頭部和載荷被串改後,生成的編碼會發生改變,因此保證了token的安全 ,還有,載荷部分可解密,因此不要往裡面放入敏感的信息(比如密碼 )

只要密鑰不洩露,別人就無法偽造任何信息

jwt的交互過程

JWT+Interceptor實現無狀態登錄和鑑權

RSA非對稱加密算算法#

由美國麻 省理工 學院三 位學者 Riv est、Sh amir 及Adleman 研 究發 展出 一套 可實 際使用 的公 開金 鑰密 碼系 統,那 就是

RSA(Rivest-Shamir-Adleman)密碼系統

jwt(是一種非對稱加密算法) JWT不一定非要搭配RSA算法,但是擁有RSA算法公鑰私鑰的特性,會使我們的業務邏輯變的簡單,做到校驗變少

  • 對稱加密,如AES(Advanced Encryption Standard) 高級加密標準
  • 基本原理:將明文分成N個組,然後使用密鑰對各個組進行加密,形成各自的密文,最後把所有的分組密文進行合併,形成最終的密文。
  • 優勢:算法公開、計算量小、加密速度快、加密效率高
  • 缺陷:雙方都使用同樣密鑰,安全性得不到保證
  • 非對稱加密,如RSA
  • 基本原理:同時生成兩把密鑰:私鑰和公鑰,私鑰隱祕保存,公鑰可以下發給信任客戶端
  • 私鑰加密,持有私鑰或公鑰才可以解密
  • 公鑰加密,持有私鑰才可解密
  • 優點:安全,難以破解
  • 缺點:算法比較耗時
  • 不可逆加密,如MD5,SHA

使用JJWT實現JWT#

JJWT(java json web token)

座標

Copy
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

創建jwt令牌#

token最終會頒發給前端,這時候就得去和前端的哥們商量它想要什麼信息

Copy
// 生成令牌,主要是用它生成載荷
JwtBuilder builder = Jwts.builder()
// 設置頭部,使用hs256加密, + key,也就是鹽
.signWith(SignatureAlgorithm.HS256,"changwu")
// 添加載荷
.setId("666") // 用戶id
.setSubject("張三") // 用戶名
.setExpiration(new Date(new Date().getTime()+60*1000)) // 過期時間
.setIssuedAt(new Date())// 登錄時間
// 添加自定義的鍵值對
.claim("role","admin");
System.out.println(builder.compact());

經過它處理的token長這個樣子, 三部組成

Copy
XXX.YYY.ZZZ

解析token#

能成功解析出結果的前提是兩次的鹽是一樣的才行

Copy
Claims map = Jwts.parser().setSigningKey("changwu")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjYiLCJzdWIiOiLlvKDkuIkiLCJleHAiOjE1NjU2MTg1MjUsImlhdCI6MTU2NTYxODQ2NSwicm9sZSI6ImFkbWluIn0.GDVfLq-ehSnMCRoxVcziXkirjOg34SUUPBK5vAEHu80")
.getBody();
System.out.println("用戶id" + map.getId());
System.out.println("用戶名" + map.getSubject());
System.out.println("token過期時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getExpiration()));
System.out.println("用戶登錄時間" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(map.getIssuedAt()));
System.out.println("用戶的角色是:"+map.get("role"));

攔截器#

注意哦,使用的是SpringMvc的攔截器,而不是Servlet的過濾器

攔截器的體系架構#

JWT+Interceptor實現無狀態登錄和鑑權

在攔截器的體系中,我們常用的是上面的來兩個

HandlerInterceptor: 是頂級接口如下:

JWT+Interceptor實現無狀態登錄和鑑權

雖然是接口, 但是擁有jdk8的特性,是默認的方法,所以允許我們挑選它的部分方法實現而不會報錯

prehandler: 請求到達控制器之間被回調,可以在這裡進行設置編碼,安全控制,權限校驗, 一般全部返回ture,表示放行

postHandler: 控制器處理請求之後生成了ModelAndView,但是未進行渲染,提供了修改ModelAndView的機會

afterCompletion: 返回給用戶ModelAndView之後執行, 用於收尾工作

第二個是HandlerInterceptorAdapter如下圖

JWT+Interceptor實現無狀態登錄和鑑權

這個適配器方法全是空實現,同樣可以滿足我們的需求,但是它同時實現了AsyncHandlerInterceptor,擁有了一個新的方法,afterConcrruentHandingStarted(request,response,handler)

這個方法會在Controller方法異步執行時開始執行, 而Interceptor的postHandle方法則是需要等到Controller的異步執行完才能執行

編碼實現#

其實到這裡改如何做已經清晰明瞭

用戶登錄,授權#

授權的很簡單

  • 用戶發送登錄請求提交form表單
  • 後端根據用戶名密碼查詢用戶的信息
  • 把用戶的信息封裝進jwt的載荷部分
  • 返回給前端token

用戶再次請求,鑑權#

後臺會有很多方法需要指定權限的人才能訪問, 所謂鑑定權限,其實就是把前端放在請求頭中的token信息解析出來,如果解析成功了,說明用戶的合法的,否則就提示前端用戶沒有權限

把token從請求頭中解析出來的過程,其實是在大量的重複性工作,所以我們放在攔截器中實現

使用攔截器兩步走

第一步,繼承HandlerInterAdapter,選擇重寫它的方法

  • 設計的邏輯,這個方法肯定要返回true, 因為後臺的方法中肯定存在大量的不需要任何權限就能訪問的方法
  • 所以這個方法的作用就是,解析出請求頭中的用戶的權限信息,重新放回到request中,
  • 這樣每個需要進行權限驗證的請求,就不需要再進行解析請求頭,而是直接使用當前回調方法的處理結果
Copy
@Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
@Autowired
JwtUtil jwtUtil;
// 在請求進入處理器之前回調這個方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 獲取請求頭
String header = request.getHeader("Authorization");
// 請求頭不為空進行解析
if (StringUtils.isNotBlank(header)) {
// 按照我們和前端約定的格式進行處理
if (header.startsWith("Bearer ")){
// 得到令牌
String token = header.substring(7);
// 驗證令牌
try{ // 令牌的解析這裡一定的try起來,因為它解析錯誤的令牌時,會報錯
// 當然你也可以在自定義的jwtUtil中把異常 try起來,這裡就不用寫了
Claims claims = jwtUtil.parseJWT(token);
String roles =(String) claims.get("roles");
System.err.println("roles=="+roles);
if (roles!=null&&"admin".equals(roles)){
request.setAttribute("role_admin",token);
}
if (roles!=null&&"user".equals(roles)){
request.setAttribute("role_user",token);
}
}catch (Exception e){
throw new RuntimeException("令牌不存在");
}
}
}
return true;
}
}

這樣 控制器中的方法需要進行權限驗證時,就免去了解析的麻煩,直接從request中獲取即可

第二步,將攔截器註冊進容器

Copy
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
RequestInterceptor requestInterceptor;
// 註冊攔截器
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/login/**");
}
}

精彩文章來源微信公眾號 java微技術

"

相關推薦

推薦中...