第一步 , 用VS建立一個空的ASP.NET Core Web Application瀏覽器
第二步 , 安裝相關的NuGet包app
經過命令在Package Manager Console執行安裝下面的包,也能夠用圖形界面來完成這一步操做。less
Install-Package Microsoft.AspNetCore.Owin -Version 1.1.2 Install-Package Nancy -PreInstall-Package Nancy.Authentication.JwtBearer
其中,Microsoft.AspNetCore.Owin和Nancy是基礎包,Nancy.Authentication.JwtBearer是等下要用到的組件包。dom
第三步 , 修改Startup,添加對Nancy的支持。ide
public class Startup{ public void Configure(IApplicationBuilder app) { app.UseOwin(x=>x.UseNancy()); } }
第四步 , 添加一個Module來驗證Nancy是否能夠正常使用測試
public class MainModule : NancyModule{ public MainModule() { Get("/",_=> { return "test"; }); } }
正常狀況下,這個時候運行項目是OK的,大體效果以下:ui
下面一步就是添加一個Bootstrapper用於啓用JwtBearer驗證。this
public class DemoBootstrapper : DefaultNancyBootstrapper{ protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); var keyByteArray = Encoding.ASCII.GetBytes("Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA=="); var signingKey = new SymmetricSecurityKey(keyByteArray); var tokenValidationParameters = new TokenValidationParameters { // The signing key must match! ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, // Validate the JWT Issuer (iss) claim ValidateIssuer = true, ValidIssuer = "http://www.cnblogs.com/catcher1994", // Validate the JWT Audience (aud) claim ValidateAudience = true, ValidAudience = "Catcher Wong", // Validate the token expiry ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; var configuration = new JwtBearerAuthenticationConfiguration { TokenValidationParameters = tokenValidationParameters }; //enable the JwtBearer authentication pipelines.EnableJwtBearerAuthentication(configuration); } }
若是使用過Nancy項目自帶的其餘認證方式(Basic,Forms和Stateless),就會發現下面的纔是關鍵,其餘的只是用於JwtBearer認證的配置參數。spa
pipelines.EnableJwtBearerAuthentication(configuration);
下面簡單介紹一下配置參數。code
配置參數主要有兩個,一個是TokenValidationParameters , 一個是Challenge 。
其中最主要的參數TokenValidationParameters,這是用來驗證客戶端傳過來的token是否合法的!
它位於Microsoft.IdentityModel.Tokens這個命名空間下面。
Challenge參數則是用於指定在Unauthorized時Http響應頭中WWW-Authenticate的值。它的默認值是Bearer
注:Challenge參數是從Microsoft.AspNetCore.Authentication.JwtBearer項目借鑑過來的。
到這裏, 咱們已經完成了對JwtBearer認證的配置和啓用,下面還要驗證這個配置是否已經生效了!
建立一個新的Module,並在這個Module中使用RequiresAuthentication。
public class SecurityModule : NancyModule{ public SecurityModule() : base("/demo") { //important this.RequiresAuthentication(); Get("/",_=> { return "JwtBearer authentication"; }); } }
注: 這裏須要引用Nancy.Security這個命名空間
到這裏,驗證的代碼也已經寫好了,當咱們訪問 http://yourdomain.com/demo 的時候
瀏覽器會提示咱們The requested resource requires user authentication , 而且在響應頭中咱們能夠看到WWW-Authenticate對應的值是Bearer。
咱們建立一個合法的token值,而後經過Fiddler再發起一次請求,看看可否正常返回咱們要的結果。
下面的代碼是生成一個測試token用的,其中的JwtSecurityToken對象應當與前面的配置同樣,才能確保token是有效的。
private string GetJwt(){ var now = DateTime.UtcNow; var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, "demo"), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64) }; //must the same as your setting in your boostrapper class var symmetricKeyAsBase64 = "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA=="; var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var jwt = new JwtSecurityToken( issuer: "http://www.cnblogs.com/catcher1994", audience: "Catcher Wong", claims: claims, notBefore: now, expires: now.Add(TimeSpan.FromMinutes(10)), signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); var response = new { access_token = encodedJwt, expires_in = (int)TimeSpan.FromMinutes(10).TotalSeconds }; return JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }); }
經過Fiddler執行這個帶上了token的請求,大體結果以下 :
能夠看到成功取到了相應的內容!
而後是本次測試用的token值相關的信息:
注:用Fiddler發起請求的時候,記得要在請求頭部加上Authorization,它的值是Bearer+空格+token值
到這裏,已經展現瞭如何使用這個JwtBearer認證的組件。
下面就介紹一下是怎麼實現的這個組件!
在繼續下面的內容以前,我假設你們對Nancy的Pipelines有所瞭解,若是不瞭解的能夠參考我之前的下面的連接
Nancy之Pipelines三兄弟(Before After OnError)
Nancy官方的Wike頁面。
由於其中的BeforePipeliine和AfterPipeline是實現這個認證組件的重要切入點。
另外,實現上還用了Nancy項目的代碼風格去編寫的代碼,因此你可能會發現與其自帶的Basic認證等寫法差很少。
從咱們上面的例子使用來講明內部實現。
在上面例子的啓動器(Bootstrapper)中,咱們有一行啓用JwtBearer認證的入口。這個入口是IPipelines的一個擴展方法。
/// <summary>/// Module requires JwtBearer authentication/// </summary>/// <param name="pipeline">Bootstrapper to enable</param>/// <param name="configuration">JwtBearer authentication configuration</param>public static void EnableJwtBearerAuthentication(this IPipelines pipeline, JwtBearerAuthenticationConfiguration configuration){ JwtBearerAuthentication.Enable(pipeline, configuration); }
在這個擴展方法中,調用了JwtBearerAuthentication這個靜態類的Enable方法,同時傳遞了當前的pipeline和JwtBearer認證的參數給這個方法。
下面是Enable方法的具體實現。
/// <summary>/// Enables JwtBearer authentication for the application/// </summary>/// <param name="pipelines">Pipelines to add handlers to (usually "this")</param>/// <param name="configuration">JwtBearer authentication configuration</param>public static void Enable(IPipelines pipelines, JwtBearerAuthenticationConfiguration configuration){ if (pipelines == null) { throw new ArgumentNullException("pipelines"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } pipelines.BeforeRequest.AddItemToStartOfPipeline(GetLoadAuthenticationHook(configuration)); pipelines.AfterRequest.AddItemToEndOfPipeline(GetAuthenticationPromptHook(configuration)); }
以BeforeRequest爲例,咱們把一個委託對象加入到了請求以前要處理的一個集合中去。這樣在每次請求以前都會去處理這個委託。
因此這裏有兩個部分。
請求處理以前的token認證
請求處理以後的響應
先來看看請求處理以前的token認證如何處理
private static Func<NancyContext, Response> GetLoadAuthenticationHook(JwtBearerAuthenticationConfiguration configuration) { return context => { Validate(context,configuration); return null; }; }
這裏也是一個空殼,用於返回AddItemToStartOfPipeline方法須要的委託對象。
真正處理token的仍是Validate這個方法。認證的處理還藉助了System.IdentityModel.Tokens.Jwt命名空間下面的JwtSecurityTokenHandler類。
private static void Validate(NancyContext context, JwtBearerAuthenticationConfiguration configuration){ //get the token from request header var jwtToken = context.Request.Headers["Authorization"].FirstOrDefault() ?? string.Empty; //whether the token value start with Bearer if (jwtToken.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { jwtToken = jwtToken.Substring("Bearer ".Length); } else { return; } //verify the token if (!string.IsNullOrWhiteSpace(jwtToken)) { try { SecurityToken validatedToken; var tokenHandler = new JwtSecurityTokenHandler(); var validatedClaims = tokenHandler.ValidateToken(jwtToken, configuration.TokenValidationParameters, out validatedToken); //var jwtSecurityToken = validatedToken as JwtSecurityToken; context.CurrentUser = validatedClaims; } catch (Exception) { } } }
要對token進行驗證,首先要知道token是從那裏來的。常規狀況下,都是將這個token放到請求頭的Authorization中。
因此第一步是要從請求頭中取出Authorization的值。這個值是必須以Bearer開頭的一個字符串。注意是Bearer加一個空格!
而咱們要驗證的部分是去掉開頭這部分以後的內容。只須要構造一個JwtSecurityTokenHandler實例並調用這個實例的ValidateToken方法,並把要驗證的token值和咱們的配置傳進去便可。
驗證成功後,最爲主要的一步是將ValidateToken方法的返回值賦給當前Nancy上下文的CurrentUser!!
當驗證失敗的時候,ValidateToken方法會拋出一個異常,這裏只catch了這個異常,並無進行其餘額外的處理。要處理無非也就是記錄日記,能夠在這裏trace一下,配合Diagnostics的使用。可是目前並無這樣作。
到這裏,Before已經OK了,如今要處理After了。
固然對於After,也是隻處理401(Unauthorized)的狀況。主要是告訴客戶端 「當前請求的資源須要用戶認證」,並告訴客戶端當前請求的資源須要那種認證類型。
private static Action<NancyContext> GetAuthenticationPromptHook(JwtBearerAuthenticationConfiguration configuration){ return context => { if (context.Response.StatusCode == HttpStatusCode.Unauthorized) { //add a response header context.Response.WithHeader(JwtBearerDefaults.WWWAuthenticate, configuration.Challenge); } }; }
一個簡單的判斷加上響應頭部的處理。
到這裏,這個JwtBearer認證的組件已經ok了。
固然這裏只介紹了Pipeline的實現,還有一個是基於NancyModule的實現,本質仍是pipeline的處理,因此這裏就不累贅了。