理解 Token 與 Session:鑒權(quán)與會話管理的區(qū)別
使用JWT進(jìn)行用戶認(rèn)證和授權(quán),而Session在一定程度上起到了輔助作用。讓我們詳細(xì)討論JWT和Session在這種結(jié)合模式中的各自作用以及為什么需要Session。
JWT的作用
- 用戶認(rèn)證: JWT包含了用戶的身份信息和權(quán)限信息,客戶端每次請求時將JWT發(fā)送給服務(wù)器,服務(wù)器通過驗(yàn)證JWT來確認(rèn)用戶身份。
- 無狀態(tài)性: JWT不需要在服務(wù)器端存儲用戶會話信息,因此服務(wù)器可以是無狀態(tài)的,便于擴(kuò)展和負(fù)載均衡。
Session的作用
- 附加的安全層: 即使JWT是無狀態(tài)的,但在某些應(yīng)用場景中,僅依賴JWT可能存在一些安全問題,例如Token的泄露或?yàn)E用。Session可以作為一個額外的安全層,確保Token即使有效,也必須在服務(wù)器的Session管理器中存在對應(yīng)的會話。
- 管理Token的生命周期: 通過Session,可以更方便地管理Token的生命周期,例如強(qiáng)制用戶重新登錄、手動注銷Token等操作。
- 控制“記住我”功能: 如果用戶選擇了“記住我”選項(xiàng),Session可以記錄這個狀態(tài),并在JWT過期后,通過Session來決定是否允許繼續(xù)使用舊的Token。
為什么需要創(chuàng)建Session
盡管JWT可以在無狀態(tài)環(huán)境中使用,但Session的引入帶來了以下好處:
- 防止Token濫用: 通過在服務(wù)器端驗(yàn)證Session,可以確保即使Token有效,也必須是經(jīng)過服務(wù)器端認(rèn)證的,從而防止Token被惡意使用。
- 支持用戶主動注銷: 當(dāng)用戶選擇注銷時,可以直接刪除服務(wù)器端的Session記錄,確保Token即使沒有過期,也無法再被使用。
- 提供更精細(xì)的控制: 通過Session,可以實(shí)現(xiàn)更精細(xì)的權(quán)限控制和用戶狀態(tài)管理,例如強(qiáng)制下線、會話過期時間控制等。
- 狀態(tài)追蹤: 在某些場景下,追蹤用戶狀態(tài)是必要的,例如監(jiān)控用戶的活躍度、登錄歷史等,這些信息可以通過Session進(jìn)行管理。
結(jié)合JWT和Session的優(yōu)勢
結(jié)合使用JWT和Session,可以同時利用兩者的優(yōu)點(diǎn),實(shí)現(xiàn)安全性和擴(kuò)展性的平衡:
- 無狀態(tài)認(rèn)證: JWT可以實(shí)現(xiàn)無狀態(tài)認(rèn)證,便于系統(tǒng)的水平擴(kuò)展和負(fù)載均衡。
- 狀態(tài)管理和安全性: Session可以提供額外的狀態(tài)管理和安全性,確保Token的使用更加安全可靠。
代碼示例
以下是一個簡化的代碼示例,展示了如何在用戶登錄時創(chuàng)建JWT和Session:
public LoginResponse login(String username, String password) throws AuthException {
// 驗(yàn)證用戶名和密碼
User user = userService.authenticate(username, password);
if (user == null) {
throw new AuthException("Invalid username or password");
}
// 生成JWT Token
String token = createJwt(user.getId(), user.getRoles());
// 創(chuàng)建會話
sessionManagerApi.createSession(token, user);
// 返回Token
return new LoginResponse(token);
}
public void createSession(String token, User user) {
LoginUser loginUser = new LoginUser();
loginUser.setToken(token);
loginUser.setUserId(user.getId());
loginUser.setRoles(user.getRoles());
sessionManagerApi.saveSession(token, loginUser);
}
在請求驗(yàn)證時,首先驗(yàn)證JWT的有效性,然后檢查Session中是否存在對應(yīng)的會話:
@Override
public DefaultJwtPayload validateToken(String token) throws AuthException {
try {
// 1. 先校驗(yàn)jwt token本身是否有問題
JwtContext.me().validateTokenWithException(token);
// 2. 獲取jwt的payload
DefaultJwtPayload defaultPayload = JwtContext.me().getDefaultPayload(token);
// 3. 如果是7天免登陸,則不校驗(yàn)session過期
if (defaultPayload.getRememberMe()) {
return defaultPayload;
}
// 4. 判斷session里是否有這個token
LoginUser session = sessionManagerApi.getSession(token);
if (session == null) {
throw new AuthException(AUTH_EXPIRED_ERROR);
}
return defaultPayload;
} catch (JwtException jwtException) {
if (JwtExceptionEnum.JWT_EXPIRED_ERROR.getErrorCode().equals(jwtException.getErrorCode())) {
throw new AuthException(AUTH_EXPIRED_ERROR);
} else {
throw new AuthException(TOKEN_PARSE_ERROR);
}
} catch (io.jsonwebtoken.JwtException jwtSelfException) {
throw new AuthException(TOKEN_PARSE_ERROR);
}
}
總結(jié)
在這個場景中,JWT用于無狀態(tài)的用戶認(rèn)證,提供便捷和擴(kuò)展性;Session作為輔助,提供額外的安全性和狀態(tài)管理。通過這種結(jié)合,可以充分利用兩者的優(yōu)點(diǎn),確保系統(tǒng)既具備高擴(kuò)展性,又能提供細(xì)致的安全控制。