舊的方法存在缺點
之前的策略是,UUID + redis + 攔截器的思路。
服務器端在驗證 roomid 和 password相匹配之后,使用 UUID 生成一個字符串作為 token ,接着往 Redis 服務中寫入一個映射(token, roomid), 設置過期時間為20分鍾, 並且把token 通過響應返回給客戶端。因此,客戶端便可以使用這個 UUID 產生的 token 來訪問。
對於需要攜帶 token 的請求,安排了一個攔截器來檢查這些請求,檢驗請求所攜帶的 token 與 roomid 是否與 Redis 中已有的映射相匹配,若匹配,說明已經授權可以可以放行,若不匹配,則拒絕請求。
缺點在於:
- 服務器端需要使用 Redis 保存許多鍵值對,占用一定的內存空間。
- 客戶端獲取的值是單純的值,當用戶太多時,UUID 的數目變得過多會加大被猜中的概率。
JWT
token 的三部分中,第一部分是頭,第二部分是負載,第三部分是簽名,一二部分都是base64加密,不能存放敏感信息。
如果客戶端私自修改過期時間,token 的驗證環節可以檢測出來。(有使用密鑰對過期時間進行加密)
如果修改客戶端 roomid,是否意味着可以冒用他人的 token?token 生成過程有把負載部分考慮在內,驗證的時候可以驗證出負載被修改。
使用 JWT 是用時間換空間,節省了服務端的空間。
使用 JWT(JSON Web Token) 的方法
maven 中添加依賴,引入 Java-jwt 項目。
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.2</version>
</dependency>
服務器有一個不能公開的密鑰。
服務器端在驗證 roomid 和 password 之后,Token 簽名算法使用密鑰根據 payload 的內容生成簽名,可以配置token 的過期時間(本項目配置的是 1 小時),token 的生成相當於服務器對驗證通過者的授權,授權其可進行 1 小時的免密碼暢行。最終得到一個 token 通過響應返回給客戶端。 客戶端可以使用這個 token 進行請求。
對於需要攜帶 token 的請求,安排了一個攔截器進行檢查,使用 token 校驗算法來檢查請求所攜帶的 token的簽名是否有效(過期或者 roomid 與簽名內容不匹配都會被認為無效)。若 token 檢驗正確,則放行,若檢驗錯誤,則拒絕請求。
下方是 token 的生成與驗證,其中 secret 是自己設定的密鑰(是一個字符串,我的密鑰可不能公開貼出來)。
token 生成過程:
public static String generate(String roomid){
String token = "";
try{
Algorithm algorithm = Algorithm.HMAC256(secret);
Map<String,Object> map = new HashMap<>();
map.put("roomid",roomid);
token = JWT.create().withIssuer("auth0")
.withPayload(map)
.withExpiresAt(new Date(System.currentTimeMillis() + (long)3600*1000))
.sign(algorithm);
}catch(JWTCreationException e){
e.printStackTrace();
}
return token;
}
token 的驗證,驗證通過則返回 roomid,驗證不通過則返回 null。
public static String validate(String roomid, String token){
DecodedJWT jwt = null;
try{
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier jwtVerifier = JWT.require(algorithm)
.withClaim("roomid",roomid)
.withIssuer("auth0").build();
jwt = jwtVerifier.verify(token);
if (jwt == null )return null;
Claim claim = jwt.getClaim("roomid");
if (claim == null )return null;
return claim.asString();
}catch (JWTVerificationException e){
return null;
}
}
