JWT頒發(fā)以及權(quán)限校驗(yàn)實(shí)踐.net core

權(quán)限設(shè)計(jì)》關(guān)于權(quán)限設(shè)計(jì)的一些方案,這里是使用.net core來(lái)實(shí)現(xiàn)jwt的授權(quán)驗(yàn)證,為了方便平時(shí)快速接入,開(kāi)箱即用。jwt有token發(fā)行端(JwtSecurityTokenExtension)和接收端驗(yàn)證(PermissionsControl)兩塊內(nèi)容,Github源碼。

Jwt Token發(fā)行端

有時(shí)候只是簡(jiǎn)單用JWT沒(méi)必要上Identity4,殺雞焉用宅牛刀,JwtSecurityTokenExtension類(lèi)庫(kù)生成token使用了微軟的System.IdentityModel.Tokens.Jwt,加密算法是對(duì)稱加密(HS256),里面有創(chuàng)建token以及刷新token的方法,講下關(guān)鍵步驟和代碼思路,具體代碼可以自行去github翻看JwtSecurityTokenExtension類(lèi)庫(kù)

token密文包含的內(nèi)容

token生成會(huì)默認(rèn)帶幾個(gè)參數(shù),上面的是頭部信息,加密方式和類(lèi)型,還有body這是必須有的兩個(gè)時(shí)間,用于校驗(yàn)端檢查token是否過(guò)期的。
生成的token可以通過(guò) http://jwt.calebb.net/ 這個(gè)網(wǎng)站進(jìn)行解析

{
    alg: "HS256",
    typ: "JWT"
}.
{
    exp:"jwt過(guò)期時(shí)間",
    nbf:"jwt生效時(shí)間"
}

你還可以你填寫(xiě)其他內(nèi)容進(jìn)去,CreateToken()方法的參數(shù)就是字典,把你需要傳遞的參數(shù)都轉(zhuǎn)化在token里一起加密。

    public interface IJwtSecurityTokenExtension
    {
        TokenOut CreateToken(Dictionary<string, string> claimsContent = null);
        TokenOut RefreshToken(string refreshToken);
    }

返回值TokenOut里有token值和刷新token的值,以及給前端驗(yàn)證token是否過(guò)期的過(guò)期時(shí)間,過(guò)期時(shí)間前端可以先校驗(yàn)一遍是否過(guò)期。

    public class TokenOut
    {
        /// <summary>
        /// token
        /// </summary>
        public string Token { get; set; }
        /// <summary>
        /// token過(guò)期時(shí)間
        /// </summary>
        public DateTime TokenExpires { get; set; }
        /// <summary>
        /// 刷新token
        /// </summary>
        public string RefreshToken { get; set; }
        /// <summary>
        /// refresh token過(guò)期時(shí)間
        /// </summary>
        public DateTime RefreshTokenExpires { get; set; }
    }

token 和 refreshToken 的用途

客戶端請(qǐng)求授權(quán)服務(wù)器會(huì)一起頒發(fā)兩個(gè)token的過(guò)期時(shí)間比較短,refreshToken的過(guò)期時(shí)間比較長(zhǎng),如果token過(guò)期了,客戶端會(huì)通過(guò)refreshToken去跟授權(quán)服務(wù)器換一個(gè)新的token。這樣就不需要頻繁登錄,因?yàn)槊看蔚卿浂夹枰脩羧ヌ顚?xiě)登錄密碼或者第三方授權(quán)。

針對(duì)這個(gè)用途,我剛開(kāi)始搞這塊的時(shí)候其實(shí)是有個(gè)疑問(wèn)的,既然用refreshToken可以置換新token,那為何不直接把token的時(shí)間設(shè)置長(zhǎng)些呢?簡(jiǎn)單粗暴何嘗不好


image.png

其實(shí)這么搞也可以,但有時(shí)候處于安全考慮,雙token可以提高那么一點(diǎn)點(diǎn)安全性(減少被截獲token時(shí)候出現(xiàn)的攻擊可能性),因?yàn)榭蛻舳撕蛻?yīng)用服務(wù)交互的頻率是遠(yuǎn)遠(yuǎn)大于客戶端和授權(quán)服務(wù)的交互,黑客劫持大概率能劫持到為客戶端和應(yīng)用服務(wù)交互的token,但因?yàn)閠oken的過(guò)期時(shí)間短,相對(duì)安全。

更換 refreshToken 的存儲(chǔ)方式

refreshToken的創(chuàng)建繼承于IRefreshTokenStore,以TryAddSingleton方式注入。你可以新寫(xiě)你自己的refreshToken存儲(chǔ)方式替換實(shí)現(xiàn),Startup重新注入便會(huì)替換(TryAddSingleton特性)

    public interface IRefreshTokenStore
    {
        RefreshTokenModel Get(string refreshToken);
        void Set(string refreshToken, RefreshTokenModel model, TimeSpan expirationTime);
    }
    
    //注入
    services.TryAddSingleton<IRefreshTokenStore, RefreshTokenStore>();

Jwt Token校驗(yàn)端

一般是引用服務(wù)權(quán)限校驗(yàn)使用,《關(guān)于權(quán)限的設(shè)》計(jì) 這里有篇文章講述幾種方案(基于資源/基于角色/基于權(quán)限

Github源碼的PermissionsControl模塊分別實(shí)現(xiàn)了這幾種方式的案例和組件。能應(yīng)對(duì)比較多種情況不用動(dòng)手?jǐn)]輪子。

image.png

使用方式

只需要ConfigureServices注入便可,但只實(shí)現(xiàn)和基于權(quán)限和基于角色。

基于資源的統(tǒng)一通用性非常差,需要自己編寫(xiě)(在WebApp項(xiàng)目下的DocumentAuthorization有demo)。

image.png

[Authorize]只能驗(yàn)證token時(shí)間有沒(méi)有過(guò)期,[UserPermissionCheck()] 和[RoleCheck("admin")] 分別驗(yàn)證權(quán)限和角色。

services.AddExtensionAuthorization(Configuration);

基于角色

如果你需要這個(gè)能識(shí)別到角色,需要在創(chuàng)建token時(shí)候加入,rol是約定

            var dic = new Dictionary<string, string>
            {
                {"rol", role},//角色
            };

            return _tokenHelper.CreateToken(dic);

[RoleCheck("admin")]就可以直接使用了

這種是針對(duì)一些角色跟權(quán)限掛鉤的,并且權(quán)限變化少的項(xiàng)目。在開(kāi)發(fā)時(shí)候定好角色就行。

基于權(quán)限

如果你需要這個(gè)能識(shí)別到權(quán)限,需要在創(chuàng)建token時(shí)候加入,per是約定

            var dic = new Dictionary<string, string>
            {
                {"per", role},//權(quán)限
            };

            return _tokenHelper.CreateToken(dic);

[PermissionCheck("update")]

和基于角色同理,可以傳入權(quán)限,但如果不傳,會(huì)根據(jù)當(dāng)前控制器名字來(lái)校驗(yàn)權(quán)限,這種方式是針對(duì)應(yīng)用于權(quán)限比較少的項(xiàng)目,因?yàn)闄?quán)限名字全部存儲(chǔ)在token里,token長(zhǎng)度會(huì)增加,導(dǎo)致每次請(qǐng)求浪費(fèi)大量網(wǎng)絡(luò)資源。

所以延伸出

[UserPermissionCheck()]

使用這個(gè)標(biāo)簽需要自行實(shí)現(xiàn)IUserPermissionStore獲取用戶權(quán)限接口,這種是把權(quán)限持久化到存儲(chǔ)媒介中,然后通過(guò)GetUserPermission()查詢出來(lái),那么token就無(wú)需帶上用戶所有權(quán)限了。其實(shí)如果做到這種程度,是否使用JWT也是個(gè)問(wèn)題,session或者每次都查詢r(jià)edis校驗(yàn)更好,見(jiàn)仁見(jiàn)智把。

    public interface IUserPermissionStore
    {
        string [] GetUserPermission(object key);
    }

這種方式更適合于微服務(wù)體系中多系統(tǒng)(子系統(tǒng))但用戶體系相同的。用戶以及其權(quán)限可以統(tǒng)一管理,但每個(gè)子系統(tǒng)的權(quán)限設(shè)計(jì)都千差萬(wàn)別,每個(gè)服務(wù)啟動(dòng)的時(shí)候都會(huì)把服務(wù)的所有權(quán)限同步到總的管理系統(tǒng),便于統(tǒng)一管理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容