springBoot整合spring security+JWT實現單點登入與許可權管理前後端分離--築基中期
阿新 • • 發佈:2020-09-04
## 寫在前面
在前一篇文章當中,我們介紹了springBoot整合spring security單體應用版,在這篇文章當中,我將介紹springBoot整合spring secury+JWT實現單點登入與許可權管理。
本文涉及的許可權管理模型是**基於資源的動態許可權管理**。資料庫設計的表有 user 、role、user_role、permission、role_permission。
單點登入當中,關於訪問者資訊的儲存有多種解決方案。如將其以key-value的形式儲存於redis資料庫中,訪問者令牌中存放key。校驗使用者身份時,憑藉訪問者令牌中的key去redis中找value,沒找到則返回“令牌已過期”,讓訪問者去(重新)認證。本文中的demo,是將訪問者資訊加密後存於token中返回給訪問者,訪問者攜帶令牌去訪問服務時,服務提供者直接解密校驗token即可。兩種實現各有優缺點。大家也可以嘗試著將本文中的demo的訪問者資訊儲存改造成存在redis中的方式。文末提供完整的程式碼及sql指令碼下載地址。
在進入正式步驟之前,我們需要了解以下知識點。
### 單點登入SSO
單點登入也稱分散式認證,指的是在有多個系統的專案中,使用者經過一次認證,即可訪問該專案下彼此相互信任的系統。
### 單點登入流程
給大家畫了個流程圖
![](https://img2020.cnblogs.com/blog/2129357/202009/2129357-20200904171000777-435357689.png)
### 關於JWT
jwt,全稱JSON Web Token,是一款出色的分散式身份校驗方案。
#### jwt由三個部分組成
1. 頭部:主要設定一些規範資訊,簽名部分的編碼格式就在頭部中宣告。
2. 有效載荷:token中存放有效資訊的部分,比如使用者名稱,使用者角色,過期時間等,但不適合放諸如密碼等敏感資料,會造成洩露。
3. 簽名:將頭部與載荷分別採用base64編碼後,用“.”相連,再加入鹽,最後使用頭部宣告的編碼型別進行編碼,就得到了簽名。
#### jwt生成的Token安全性分析
想要使得token不被偽造,就要確保簽名不被篡改。然而,其簽名的頭部和有效載荷使用base64編碼,這與明文無異。因此,我們只能在鹽上做手腳了。我們對鹽進行非對稱加密後,在將token發放給使用者。
### RSA非對稱加密
1. 基本原理:同時生成兩把金鑰:私鑰和公鑰,私鑰隱祕儲存,公鑰可以下發給信任客戶端 。
- 公鑰加密:只有私鑰才能解密
- 私鑰加密:私鑰或者公鑰都能解密
2. 優缺點:
- 優點:安全、難以破解
- 缺點:耗時,但是為了安全,這是可以接受的
### SpringSecurity+JWT+RSA分散式認證思路分析
通過之前的學習,我們知道了spring security主要是基於過濾器鏈來做認證的,因此,如何打造我們的單點登入,突破口就在於spring security中的認證過濾器。
#### 使用者認證
在分散式專案當中,現在大多數都是前後端分離架構設計的,因此,我們需要能夠接收POST請求的認證引數,而不是傳統的表單提交。因此,我們需要修改修
改UsernamePasswordAuthenticationFilter過濾器中attemptAuthentication方法,讓其能夠接收請求體。
關於spring security的認證流程分析,大家可以參考我上一篇文章《[Spring Security認證流程分析--練氣後期](https://juejin.im/post/6863693023314706446)》。
另外,預設情況下,successfulAuthentication 方法在通過認證後,直接將認證資訊放到伺服器的session當中就ok了。而我們分散式應用當中,前後端分離,禁用了session。因此,我們需要在認證通過後生成token(載荷內具有驗證使用者身份必要的資訊)返回給使用者。
#### 身份校驗
預設情況下,BasicAuthenticationFilter過濾器中doFilterInternal方法校驗使用者是否登入,就是看session中是否有使用者資訊。在分散式應用當中,我們要修改為,驗證使用者攜帶的token是否合法,並解析出使用者資訊,交給SpringSecurity,以便於後續的授權功能可以正常使用。
## 實現步驟
(預設大家一已經建立好了資料庫)
### 第一步:建立一個springBoot的project
這個父工程主要做依賴的版本管理。
其pom.xml檔案如下
```xml
```
### 第二步:建立三個子模組
其中,**common**模組作為公共模組存在,提供基礎服務,包括token的生成、rsa加密金鑰的生成與使用、Json序列化與反序列化。
**authentication-service**模組提供單點登入服務(使用者認證及授權)。
**product-service**模組模擬一個子系統。它主要負責提供介面呼叫和校驗使用者身份。
#### 建立**common**模組模組
#####修改pom.xml,新增jwt、json等依賴
pom.xml
```xml
```
##### 建立一個JSON工具類
```java
**json工具類
* @author 賴柄灃 [email protected]
* @version 1.0
* @date 2020/9/2 22:28
*/
public class JsonUtils {
public static final ObjectMapper MAPPER = new ObjectMapper();
private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
private JsonUtils() {
}
public static String toString(Object obj) {
if (obj == null) {
return null;
}
if (obj.getClass() == String.class) {
return (String) obj;
}
try {
return MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.error("json序列化出錯:" + obj, e);
return null;
}
}
public