天天看點

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

群友回報:

群裡有小夥伴回報,在Swagger使用的時候報錯,無法看到清單,這裡我說下如何調試和主要問題:

1、如果遇到問題,這樣的:

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
請在浏覽器 =》 F12 ==》 console 控制台 ==》點選錯誤資訊位址
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

或者直接連結http://localhost:xxxxx/swagger/v1/swagger.json,就能看到錯誤了

2、主要問題就是同一個controller中的同一個請求特性(注意[HttpGet]和[HttpGet("{id}")]是兩個) ,不能同名

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

主要修改下路由,然後配合修改名字就行

[Route("api/[controller]/[action]")]

4、或者直接在方法上增加路由

    [HttpPost]
        [Route("newPost")]
        public void Post([FromBody]string value)
        {
        }      

WHY

書接上文,在前邊的兩篇文章中,我們簡單提到了接口文檔神器Swagger,《

從零開始搭建自己的前後端分離【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】架構之三 || Swagger的使用 3.1

》、《

從零開始搭建自己的前後端分離【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】架構之四 || Swagger的使用 3.2

》,兩個文章中,也對常見的幾個問題做了簡單的讨論,最後還剩下一個小問題,

如何給接口實作權限驗證?

其實關于這一塊,我思考了下,因為畢竟我的項目中是使用的vue + api 搭建一個前台展示,大部分頁面都沒有涉及到權限驗證,本來要忽略這一章節,可是猶豫再三,還是給大家簡單分析了下,個人還是希望陪大家一直搭建一個較為強大的,隻要是涉及到後端那一定就需要 登陸=》驗證了,本文主要是參考網友https://www.cnblogs.com/RayWang/p/9255093.html的思路,我自己稍加改動,大家都可以看看。

根據維基百科定義,JWT(讀作 [/dʒɒt/]),即JSON Web Tokens,是一種基于JSON的、用于在網絡上聲明某種主張的令牌(token)。JWT通常由三部分組成: 頭資訊(header), 消息體(payload)和簽名(signature)。它是一種用于雙方之間傳遞安全資訊的表述性聲明規範。JWT作為一個開放的标準(RFC 7519),定義了一種簡潔的、自包含的方法,進而使通信雙方實作以JSON對象的形式安全的傳遞資訊。

以上是JWT的官方解釋,可以看出JWT并不是一種隻能權限驗證的工具,而是一種标準化的資料傳輸規範。是以,隻要是在系統之間需要傳輸簡短但卻需要一定安全等級的資料時,都可以使用JWT規範來傳輸。規範是不因平台而受限制的,這也是JWT做為授權驗證可以跨平台的原因。

如果了解還是有困難的話,我們可以拿JWT和JSON類比:

JSON是一種輕量級的資料交換格式,是一種資料層次結構規範。它并不是隻用來給接口傳遞資料的工具,隻要有層級結構的資料都可以使用JSON來存儲和表示。當然,JSON也是跨平台的,不管是Win還是Linux,.NET還是Java,都可以使用它作為資料傳輸形式。

1)用戶端向授權服務系統發起請求,申請擷取“令牌”。

2)授權服務根據使用者身份,生成一張專屬“令牌”,并将該“令牌”以JWT規範傳回給用戶端

3)用戶端将擷取到的“令牌”放到http請求的headers中後,向主服務系統發起請求。主服務系統收到請求後會從headers中擷取“令牌”,并從“令牌”中解析出該使用者的身份權限,然後做出相應的處理(同意或拒絕傳回資源)

一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證

在之前的搭建中,swagger已經基本成型,其實其功能之多,不是我這三篇所能寫完的,想要添權重限,先從服務開始

在ConfigureServices中,增加以下代碼

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
#region Token綁定到ConfigureServices 

 //添加header驗證資訊 

 var security = new Dictionary> { { "Blog.Core", new string[] { } }, };

                c.AddSecurityRequirement(security);

                //方案名稱“Blog.Core”可自定義,上下一緻即可

                c.AddSecurityDefinition("Blog.Core", new ApiKeyScheme

                {

                    Description = "JWT授權(資料将在請求頭中進行傳輸) 直接在下框中輸入{token}\"",

                    Name = "Authorization",//jwt預設的參數名稱

                    In = "header",//jwt預設存放Authorization資訊的位置(請求頭中)

                    Type = "apiKey"

                });

                #endregion      

View Code

 最終的是這樣的

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
/// <summary>
        /// ConfigureServices 方法
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            #region Swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info
                {
                    Version = "v0.1.0",
                    Title = "Blog.Core API",
                    Description = "架構說明文檔",
                    TermsOfService = "None",
                    Contact = new Swashbuckle.AspNetCore.Swagger.Contact { Name = "Blog.Core", Email = "[email protected]", Url = "https://www.jianshu.com/u/94102b59cc2a" }
                });

                //就是這裡

                #region 讀取xml資訊
                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "Blog.Core.xml");//這個就是剛剛配置的xml檔案名
                var xmlModelPath = Path.Combine(basePath, "Blog.Core.Model.xml");//這個就是Model層的xml檔案名
                c.IncludeXmlComments(xmlPath, true);//預設的第二個參數是false,這個是controller的注釋,記得修改
                c.IncludeXmlComments(xmlModelPath);
                #endregion

                #region Token綁定到ConfigureServices
                //添加header驗證資訊
                //c.OperationFilter<SwaggerHeader>();
                var security = new Dictionary<string, IEnumerable<string>> { { "Blog.Core", new string[] { } }, };
                c.AddSecurityRequirement(security);
                //方案名稱“Blog.Core”可自定義,上下一緻即可
                c.AddSecurityDefinition("Blog.Core", new ApiKeyScheme
                {
                    Description = "JWT授權(資料将在請求頭中進行傳輸) 直接在下框中輸入{token}\"",
                    Name = "Authorization",//jwt預設的參數名稱
                    In = "header",//jwt預設存放Authorization資訊的位置(請求頭中)
                    Type = "apiKey"
                }); 
                #endregion


            });
            #endregion

            #region Token服務注冊
            services.AddSingleton<IMemoryCache>(factory =>
             {
                 var cache = new MemoryCache(new MemoryCacheOptions());
                 return cache;
             });
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Admin", policy => policy.RequireClaim("AdminType").Build());//注冊權限管理,可以自定義多個
            }); 
            #endregion
        }      

然後執行代碼,就可以看到效果

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

圖 1

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

圖 2

它的作用就是,每次請求時,從Header封包中,擷取密鑰token,這裡根據token可以進一步判斷相應的權限等。

接下來,就是在項目中添加五個檔案,如下圖

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

,圖 3

具體來說:

1:BlogCoreMemoryCache

這裡是簡單的一個緩存的使用,在以後的Redis中,也可以配合使用,這裡是先借鑒大神的,以後我會擴充,然後并結合Redis,具體看Git上的代碼,這裡不做詳細說明

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
public class RayPIMemoryCache
    {
        public static MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());

        /// <summary>
        /// 驗證緩存項是否存在
        /// </summary>
        /// <param name="key">緩存Key</param>
        /// <returns></returns>
        public static bool Exists(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            object cached;
            return _cache.TryGetValue(key, out cached);
        }

        /// <summary>
        /// 擷取緩存
        /// </summary>
        /// <param name="key">緩存Key</param>
        /// <returns></returns>
        public static object Get(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            return _cache.Get(key);
        }

        /// <summary>
        /// 添加緩存
        /// </summary>
        /// <param name="key">緩存Key</param>
        /// <param name="value">緩存Value</param>
        /// <param name="expiresSliding">滑動過期時長(如果在過期時間内有操作,則以目前時間點延長過期時間)</param>
        /// <param name="expiressAbsoulte">絕對過期時長</param>
        /// <returns></returns>
        public static bool AddMemoryCache(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }
            _cache.Set(key, value,
                    new MemoryCacheEntryOptions()
                    .SetSlidingExpiration(expiresSliding)
                    .SetAbsoluteExpiration(expiressAbsoulte)
                    );

            return Exists(key);
        }
    }      

2:BlogCoreToken,主要方法,擷取JWT字元串并存入緩存中

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
public class BlogCoreToken
    {

        public BlogCoreToken()
        {
        }

        /// <summary>
        /// 擷取JWT字元串并存入緩存
        /// </summary>
        /// <param name="tm"></param>
        /// <param name="expireSliding"></param>
        /// <param name="expireAbsoulte"></param>
        /// <returns></returns>
        public static string IssueJWT(TokenModel tokenModel, TimeSpan expiresSliding, TimeSpan expiresAbsoulte)
        {
            DateTime UTC = DateTime.UtcNow;
            Claim[] claims = new Claim[]
            {
                new Claim(JwtRegisteredClaimNames.Sub,tokenModel.Sub),//Subject,
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),//JWT ID,JWT的唯一辨別
                new Claim(JwtRegisteredClaimNames.Iat, UTC.ToString(), ClaimValueTypes.Integer64),//Issued At,JWT頒發的時間,采用标準unix時間,用于驗證過期
            };

            JwtSecurityToken jwt = new JwtSecurityToken(
            issuer: "Blog.Core",//jwt簽發者,非必須,自定義
            audience: tokenModel.Uname,//jwt的接收該方,非必須
            claims: claims,//聲明集合
            expires: UTC.AddHours(12),//指定token的生命周期,unix時間戳格式,非必須
            signingCredentials: new Microsoft.IdentityModel.Tokens
                .SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("Blog.Core's Secret Key")), SecurityAlgorithms.HmacSha256));

            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

            RayPIMemoryCache.AddMemoryCache(encodedJwt, tokenModel, expiresSliding, expiresAbsoulte);//将JWT字元串,令牌實體,存入緩存
            return encodedJwt;
        }

      
    }      

3:TokenAuth,這是一個中間件,每次網絡請求的時候,都走這裡,作為封包擷取判斷,并防篡改

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
public class TokenAuth
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly RequestDelegate _next;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="next"></param>
        public TokenAuth(RequestDelegate next)
        {
            _next = next;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext httpContext)
        {
            var headers = httpContext.Request.Headers;
            //檢測是否包含'Authorization'請求頭,如果不包含傳回context進行下一個中間件,用于通路不需要認證的API
            if (!headers.ContainsKey("Authorization"))
            {
                return _next(httpContext);
            }

            var tokenStr = headers["Authorization"];
            try
            {
                string jwtStr = tokenStr.ToString().Trim();

                //如何存在Authorization,但是和緩存的不一樣,那就是被篡改了
                if (!RayPIMemoryCache.Exists(jwtStr))
                {
                    return httpContext.Response.WriteAsync("非法請求");
                }

                TokenModel tm = ((TokenModel)RayPIMemoryCache.Get(jwtStr));

                //提取tokenModel中的Sub屬性進行authorize認證
                List<Claim> lc = new List<Claim>();
                Claim c = new Claim(tm.Sub+"Type", tm.Sub);
                lc.Add(c);

                ClaimsIdentity identity = new ClaimsIdentity(lc);
                ClaimsPrincipal principal = new ClaimsPrincipal(identity);
                httpContext.User = principal;

                return _next(httpContext);
            }
            catch (Exception)
            {
                return httpContext.Response.WriteAsync("token驗證異常");
            }
        }
    }      

4:上邊的方法中都會用到一個TokenModel,自己簡單寫一個,也可以是你登陸的時候的使用者實體類,或者其他,

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
///

    /// 令牌類

    ///

    public class TokenModel

    {

        publicTokenModel()

        {

            this.Uid = 0;

        }

        ///

        /// 使用者Id

        ///

        public long Uid { get; set; }

        ///

        /// 使用者名

        ///

        public string Uname { get; set; }

        ///

        /// 手機

        ///

        public string Phone { get; set; }

        ///

        /// 頭像

        ///

        public string Icon { get; set; }

        ///

        /// 昵稱

        ///

        public stringUNickname { get; set; }

        ///

        /// 簽名

        ///

        public string Sub { get; set; }      

5:将四個檔案都添加好後,最後兩步

1、然後再Startup的Configure中,将TokenAuth注冊中間件

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

圖 6

2、在需要權重限的頁面中,增加特性

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

這個時候,你運作項目,發現之前寫的都報錯了,

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

圖 7

别慌!是因為每次操作請求,都會經過TokenAuth 中的Invoke方法,方法中對Header資訊進行過濾,因為現在Header中,并沒有相應的配置資訊,看到這裡,你就想到了,這個特别像我們常見的[HttpGet]等特性,沒錯!在.Net Core 中,到處都可以看到AOP程式設計,真的特别強大。

這個時候我們就用到了最開始的那個權限按鈕

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

,圖 8

沒錯就是這裡,但是我們方法寫好了,那Token如何擷取呢,别急,我們建立一個LoginController,來模拟一次登陸操作,簡單傳遞幾個參數,将使用者角色和緩存時間傳遞,然後生成Token,并生成到緩存中,為之後做準備。

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE
  [HttpGet]

        [Route("Token")]

        public JsonResult GetJWTStr(long id=1, string sub="Admin", int expiresSliding = 30, int expiresAbsoulute = 30)

        {

          TokenModel tokenModel = new TokenModel();

            tokenModel.Uid = id;

            tokenModel.Sub = sub;

            DateTime d1 = DateTime.Now;

            DateTime d2 = d1.AddMinutes(expiresSliding);

            DateTime d3 = d1.AddDays(expiresAbsoulute);

            TimeSpan sliding = d2 - d1;

            TimeSpan absoulute = d3 - d1;

            string jwtStr = BlogCoreToken.IssueJWT(tokenModel, sliding, absoulute);

            return Json(jwtStr);

        }      

這個時候我們就得到了我們的Token

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

圖 9

然後粘貼到我們的上圖權限視窗中,還記得麼

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

圖 10

接下來,你再調用視窗,就發現都可以辣!

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)

1、還是和上一種方法類似,做了封裝,在項目檔案夾AuthHelper下,建立一個OverWrite檔案夾,然後建立JwtHelper類

這個類,主要是是生成Token,和解析Token

namespace Blog.Core.AuthHelper
{
    public class JwtHelper
    {
        public static string secretKey { get; set; } = "sdfsdfsrty45634kkhllghtdgdfss345t678fs";
        /// <summary>
        /// 頒發JWT字元串
        /// </summary>
        /// <param name="tokenModel"></param>
        /// <returns></returns>
        public static string IssueJWT(TokenModelJWT tokenModel)
        {
            var dateTime = DateTime.UtcNow;
            var claims = new Claim[]
            {
                new Claim(JwtRegisteredClaimNames.Jti,tokenModel.Uid.ToString()),//Id
                new Claim("Role", tokenModel.Role),//角色
                new Claim(JwtRegisteredClaimNames.Iat,dateTime.ToString(),ClaimValueTypes.Integer64)
            };
            //秘鑰
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtHelper.secretKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
          
            var jwt = new JwtSecurityToken(
                issuer: "Blog.Core",
                claims: claims, //聲明集合
                expires: dateTime.AddHours(2),
                signingCredentials: creds);

            var jwtHandler = new JwtSecurityTokenHandler();
            var encodedJwt = jwtHandler.WriteToken(jwt);

            return encodedJwt;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="jwtStr"></param>
        /// <returns></returns>
        public static TokenModelJWT SerializeJWT(string jwtStr)
        {
            var jwtHandler = new JwtSecurityTokenHandler();
            JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
            object role = new object(); ;
            try
            {
                jwtToken.Payload.TryGetValue("Role", out role);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            var tm = new TokenModelJWT
            {
                Uid = long.Parse(jwtToken.Id),
                Role = role.ToString(),
            };
            return tm;
        }
    }

    /// <summary>
    /// 令牌
    /// </summary>
    public class TokenModelJWT
    {
        /// <summary>
        /// Id
        /// </summary>
        public long Uid { get; set; }
        /// <summary>
        /// 角色
        /// </summary>
        public string Role { get; set; }
     
    }
}      

2、還是在OverWrite檔案夾中,建立中間件JwtTokenAuth.cs,主要作用和之前一樣

public class JwtTokenAuth
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly RequestDelegate _next;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="next"></param>
        public JwtTokenAuth(RequestDelegate next)
        {
            _next = next;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext httpContext)
        {
            //檢測是否包含'Authorization'請求頭
            if (!httpContext.Request.Headers.ContainsKey("Authorization"))
            {
                return _next(httpContext);
            }
            var tokenHeader = httpContext.Request.Headers["Authorization"].ToString();

            TokenModelJWT tm = JwtHelper.SerializeJWT(tokenHeader);

            //授權
            var claimList = new List<Claim>();
            var claim = new Claim(ClaimTypes.Role, tm.Role);
            claimList.Add(claim);
            var identity = new ClaimsIdentity(claimList);
            var principal = new ClaimsPrincipal(identity);
            httpContext.User = principal;

            return _next(httpContext);
        }

    }      

3、在項目啟動類的配置服務ConfigureService中,新增認證部分

#region 認證
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
                .AddJwtBearer(o =>
                {
                    o.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = "Blog.Core",
                        ValidAudience = "wr",
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(JwtHelper.secretKey)),

                      
                        RequireSignedTokens = true,
                        // 将下面兩個參數設定為false,可以不驗證Issuer和Audience,但是不建議這樣做。
                        ValidateAudience = false,
                        ValidateIssuer = true,
                        ValidateIssuerSigningKey = true,
                        // 是否要求Token的Claims中必須包含 Expires
                        RequireExpirationTime = true,
                        // 是否驗證Token有效期,使用目前時間與Token的Claims中的NotBefore和Expires對比
                        ValidateLifetime = true
                    };
                });
            #endregion      

登陸,就是驗證使用者登陸以後,通過個人資訊(使用者名+密碼),調取資料庫資料,根據權限,生成一個令牌

認證,就是根據登陸的時候,生成的令牌,檢查其是否合法,這個主要是證明沒有被篡改

授權,就是根據令牌反向去解析出的使用者身份,回應目前http請求的許可,表示可以使用目前接口,或者拒絕通路

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

4、修改services.AddAuthorization方法,通過增加角色的方式來配置

    services.AddAuthorization(options =>
            {
                options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());
                options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());
                options.AddPolicy("AdminOrClient", policy => policy.RequireRole("Admin,Client").Build());
            });      

5、記得修改configure中的中間件

app.UseMiddleware<JwtTokenAuth>();      

如果沒有權限會是這樣的

從壹開始前後端分離【 .NET Core2.0 +Vue2.0 】架構之五 || Swagger的使用 3.3 JWT權限驗證,兩種方法群友回報:WHY如何給接口實作權限驗證?一、通過Jwt擷取Token,并通過緩存記錄,配合中間件實作驗證二、通過認證授權的形式,實作驗證,去掉緩存(此部分代碼周一Git更新)WHATNEXTCODE

WHAT

這一篇呢,寫的比較潦草,主要是講如何使用,具體的細節知識,還是大家摸索,還是那句話,這裡隻是抛磚引玉的作用喲,通過閱讀本文,你會了解到,什麼是JWT,如何添加配置.net core 中間件,如何使用Token驗證,在以後的項目裡你就可以在登陸的時候,調用Token,傳回用戶端,然後判斷是否有相應的接口權限。

NEXT

好啦!項目準備階段就這麼結束了,以後咱們就可以直接用swagger來調試了,而不是沒錯都用F5運作等,接下來我們就要正式開始搭建項目了,主要采用的是泛型倉儲模式 Repository+Service,也是一種常見的模式。

CODE

https://github.com/anjoy8/Blog.Core.git

繼續閱讀