討論兩種Redis中Token的存儲方式

摘要:本文討論一個問題:存儲token時,token與對應用戶id誰來作爲key? 問題起源問題起源於要給公司的後臺管理系統添加權限管理,選用的是開源框架shiro,而原本系統上是採用token做了登錄校驗的。

本文分享自華爲雲社區《討論兩種Redis中Token的存儲方式》,作者:洛葉飄。

問題起源

問題起源於要給公司的後臺管理系統添加權限管理,選用的是開源框架shiro,而原本系統上是採用token做了登錄校驗的。

我所採用的shiro驗證方式是,每次接口請求,根據token來獲取用戶id,然後通過shiro中的登錄驗證機制來進行權限校驗。因此,“根據token獲取用戶id”就要求在存儲用戶token時,以token爲鍵值key,以用戶ID爲value值。

然而此時面臨一個問題是,系統原本的token存儲方式如下,我們稱之爲第一種:用戶ID爲key。

cache.set(TOKEN_PREFIX + userid, token);

這就需要我做出判斷,需不需要修改token的存儲方式爲下面的形式:我們稱之爲第二種:token爲key。

cache.set(TOKEN_PREFIX + token, userid);

思考

第一個問題,兩種方式是否都能夠實現需求功能?

我們需要實現的功能包括:

  1. 登錄驗證
  2. shiro中的權限驗證

登錄驗證

對於"用戶ID爲key"的方式,需要前端傳遞用戶id+token兩個值,驗證登錄狀態需要我們根據前端傳遞的用戶ID,獲取數據庫中存儲的token,與前端傳遞的token進行校驗,如果一致,則校驗通過,否則返回錯誤信息,提示用戶需要重新登錄等等。

對於“token爲key”的方式,前端至少需要傳遞token一個值,根據前端傳遞的token,獲取數據庫中存儲的用戶ID,如果能獲取到,則校驗通過,否則提示用戶token已過期,需要用戶重新登錄等等。

shiro中的權限驗證

shiro中的權限驗證,涉及到具體的實現機制,以token爲key的方式,就以我們的真實實現爲例:

// shiro登錄代碼:
Subject s = SecurityUtils.getSubject();
JWTToken jwtToken = new JWTToken(token);
subject.login(jwtToken);
// 實現AuthenticationToken的類:
import org.apache.shiro.authc.AuthenticationToken;

public class JWTToken implements AuthenticationToken {
    private static final long serialVersionUID = 1L;
 
    // 密鑰
    private String token;

    public JWTToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

/**
* 自定義的登錄驗證類:
*/
public class ShiroDbRealm extends AuthorizingRealm{
    /**
     * 重寫shiro的token
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 角色,權限認證
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //這裏可以連接數據庫根據用戶賬戶進行查詢用戶角色權限等信息
        return simpleAuthorizationInfo;
    }
 
    /**
     * 自定義認證
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
         // 解密獲得userid,用於和數據庫進行對比
         // getUserId實際就是通過token,在數據庫中取對應的userid
        Integer userid = JwtUtils.getUserId(token);
        if (tuserid == null) {
            throw new AuthenticationException("token 校驗失敗");
        }
        return new SimpleAuthenticationInfo(token, token, getName());
    }
}

如果採用userid爲key的方式,不難實現,也修改其實現方式,

第二個問題,兩種方式哪一種傳輸的數據量更少?

第一種方式需要前端每次請求都傳遞token+userid;而第二種實際上可以只傳遞token,後臺根據token解密(或數據庫查找)來獲取用戶信息。

第三個問題,兩種方式哪種更安全?

兩種方式的安全應該是一樣的,核心是後臺通過數據庫保存token與userid的對應信息。

個人意見

個人比較細化第二種,以token爲key的方式,首先,前端傳遞簡單,只需要傳遞token即可;二是後端通過這種方式,可以統一當前登錄人的獲取方式,而不是每次在接口中獲取header中的用戶id。

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章