1. 程式人生 > >【譯】使用Jwt身份認證保護 Asp Net Core Web Api

【譯】使用Jwt身份認證保護 Asp Net Core Web Api

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                                                                                                                       

原文出自Rui Figueiredo的部落格,原文連結《Secure a Web Api in ASP.NET Core》


摘要:這篇文章闡述瞭如何使用 Json Web Token (Jwt)方式 來配置身份驗證中介軟體。這種方式十分適合移動App 後端等不使用cookie的後端程式。

網路上有許多資源可以教你如何保護ASP.NET Core Web應用程式。我寫過一些,例如 ASP.NET Core Identity From Scratch , External Login Providers in ASP.NET Core and Facebook Authentiation with ASP.NET Core.

不過對於保護Asp.Net WebApi,網路上有用的資訊似乎不多。所以在這篇博文中,我將介紹如何使用Json Web Tokens(JWT)來保護ASP.NET Core中的Web Api。我在github中有一個演示專案,你可以照著它來做。

使用token替代cookie

在一個Web應用程式中,如果你不打算使用供應外部呼叫(例如一個移動應用程式)的API,那麼它通常使用一個cookie來表示一個已經登入的使用者。

一般的流程是:使用者單擊登入,進入登入頁面,輸入有效憑證後,伺服器傳送給使用者瀏覽器的響應包含一個帶有加密資訊的 Set-Cookie 頭。

cookie會被設定上domain 例如 blinkingcaret.com,每次瀏覽器向這個domain傳送請求時,設定在這個domain上的cookie也會被帶上。

在伺服器上,cookie將被解密,然後使用解密後的內容來建立使用者的 Identity

如果客戶端是一個瀏覽器,這種方式將會非常非常適合。不過當我們的客戶端是一個移動應用程式時候,那就另當別論了。

JWT

我們可以使用什麼來代替cookie呢?沒錯就是token。token也代表使用者,但是當我們使用它的時候,我們不再依賴於瀏覽器的內建機制以及用它和cookie打交道。

我們必須明確地向伺服器要一個token,我們自己將它儲存在某個地方,然後在每個請求傳送時手動帶上它。有一些方法可以使這個儘可能簡單快捷,我會在後面討論其中的一些方法。

我將在這裡討論的token格式是JWT。

JWT代表Json Web Token。JWTtoken具有以下格式 base64-encoded-header.base64-encoded-payload.signature

一個heder的例子是

{    “alg”: “HS265”,    “typ”: “JWT”}

payload包含一系列 claims,例如:

{    "name": "Rui",    "admin": true}

最後,通過採用“base64(header).base64(payload)”建立簽名,並使用頭部指定的演算法對簽名其進行加密。例如 HMAC-SHA256。簽名部分會用到一個儲存在server上的金鑰,這個金鑰是不會發給客戶端的。

下面是一個真正的JWT的例子:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoicnVpIiwic3ViIjoidGVzdCIsIm5iZiI6MTUwMzYxNDU4NSwiZXhwIjoxNTA2MDMzNzg1LCJpc3MiOiJibGlua2luZ2NhcmV0IHN0cyIsImF1ZCI6ImJsaW5raW5nY2FyZXQgYXBwIn0.F7PFoYcQXez3zV98BFKLpyON6d_1p-6IAeihZRSv0VM

你必須注意的是,JWT中包含的資訊沒有加密。為了獲得有效payload,你只需要base64解碼。你甚至可以從你的開發者工具控制檯(例如在Chrome中)這樣做。使用atob方法並將payload作為引數傳遞。你會得到解密後的JSON 。signature只能保證如果有人篡改了payload,那麼signature將會失效。如果有人想成功替換有效載荷並生成有效的token,他們需要知道簽名中使用的金鑰,但是該金鑰永遠不會被髮送到客戶端。

所以,當你想往payload裡放一些東西的時候,你一定要知道上面這些

譯者注:就是不要把敏感資訊放在payload裡,比如:密碼。

在 ASP.NET Core 中使用JWT

要在ASP.NET Core中使用JWT,我們需要知道如何手動建立JWTtoken,如何驗證它們以及如何建立端點以便客戶端應用程式可以獲得它們。

如何建立JWTtoken

首先你需要安裝nuget包System.IdentityModel.Tokens.Jwt

$ dotnet add package System.IdentityModel.Tokens.Jwt

然後建立一個金鑰。我們將使用 symmetric key(譯者注:對稱金鑰),程式碼如下:

var secretKey = new SymmetricSecurityKey(Endoding.UTF8.GetBytes("a secret that needs to be at least 16 characters long"));

譯者注:a secret that needs to be at least 16 characters long=>一個至少需要16個字元的密碼,在驗證簽名時還會用到。

我們的token將包含一組claims。所以讓我們建立它們:

var claims = new Claim[] {   
 new Claim(ClaimTypes.Name, "John"),
    new Claims(JwtRegisteredClaimNames.Email, "[email protected]")}

我已經使用了兩種claim型別 :

  1. ClaimTypes(System.Security.Claims)

  2. JwtRegisteredClaimNames(System.IdentityModel.Tokens.Jwt)

要強調的是JwtRegisteredClaimNames包含在JWT RFC中列舉的claims中。如果你打算使用不同程式語言或者框架生成的token,那麼為了相容性,你應該儘可能的使用這個。不過,有一些宣告型別可以在ASP.NET中啟用某些功能。例如,ClaimTypes.Name 是使用者名稱(User.Identity.Name)的預設宣告型別。另一個例子是ClaimTypes.Role,如果你在Authorize屬性中使用Roles屬性(例如[Authorize(Roles =“Administrator”)]),這個宣告將會被檢查用來確認許可權。

在建立我們想要在token中編碼的claims列表之後,我們可以建立token本身,程式碼如下:

var token = new JwtSecurityToken(  
   issuer:
"your app",  
   audience:
"the client of your app",  
   claims:
claims,  
   notBefore:
DateTime.Now,  
   expires:
DateTime.Now.AddDays(28),  
   signingCredentials:
new SigningCredentials(key, SecurityAlgorithms.HmacSha256));

這裡有一些我之前沒有提到的概念,即發issue,audience和expiration dates。

譯者注: 發行者,受眾/聽眾,過期時間

發行者表示生成token的實體,在這個例子裡它是ASP.NET Core Web應用程式。audience代表將要使用這些token的實體,例如 client。如果你依靠第三方建立token(不是現在所要用到的),這個issue和audience是重要的。驗證token時,你可以驗證issue和audience。

notBefore 和 expire 定義了 token的有效時間區間,在notBefore之後expire之前。

最後在signedCredentials中指定使用哪個安全金鑰和什麼演算法來建立簽名。在這個例子中我們使用了HMAC-SHA256。

如果你不關心issue和audience(在JWT規範中是可選的),你可以使用接受JwtSecurityHeader和JwtSecurityPayload的JwtSecurityToken的更簡單的建構函式過載。不過你必須手動將expires和notBefore宣告新增到有效內容中,例如:

var claims = new Claim[] {    new Claim(ClaimTypes.Name, "John"),    new Claims(JwtRegisteredClaimNames.Email, "[email protected]"),    new Claim(JwtRegisteredClaimNames.Exp, $"{new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds()}"),    new Claim(JwtRegisteredClaimNames.Nbf, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}")        }var token = new JwtSecurityToken(new JwtHeader(new SigningCredentials(key, SecurityAlgorithms.HmacSha256)), new JwtPayload(claims));

請注意Exp(expires)和Nbf(notBefore)宣告的值是一個Unix時間的字串。將DateTime轉換為該格式的最簡單方法是使用DateTimeOffset

在建立JwtSecurityToken的例項後,實際生成token的方法是呼叫JwtSecurityTokenHandler例項的WriteToken方法,並將JwtSecurityToken作為引數傳遞:

string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);

建立獲取token的端點

現在我們知道如何建立我們的JWT token了,我們還需要一種方法來讓客戶端獲得它們。最簡單的方法是建立一個期望釋出請求的web api controller action 接受一個Post請求,例如下面的程式碼:

public class TokenController : Controller{    [Route("/token")]    [HttpPost]            public IActionResult Create(string username, string password)    {    
   if (IsValidUserAndPasswordCombination(username, password))      
         return new ObjectResult(GenerateToken(username));    
      return BadRequest();    }//...

IsValidUserAndPasswordCombination中,你可以來驗證使用者的憑據例如使用例如ASP.NET Identity(如果你需要參考資料來學習ASP.NET Identity,你可以看這篇部落格 ASP.NET Identity Core From Scratch)。

GenerateToken我們剛剛在上一節中描述過。

驗證使用者,並使其登陸

現在我們有了一種發行token的方法,我們還需要一種方法來驗證它們。我們將使用ASP.NET Core的身份驗證中介軟體,並將其配置為可接受JWT token。
Microsoft.AspNetCore.Authentication.JwtBearer NuGet包新增到你的專案。

$ dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

接下來開啟Startup.cs並更新ConfigureServices方法:

public void ConfigureServices(IServiceCollection services){    //...    services.AddAuthentication(options => {        options.DefaultAuthenticateScheme = "JwtBearer";        options.DefaultChallengeScheme = "JwtBearer";                })    .AddJwtBearer("JwtBearer", jwtBearerOptions =>    {                                jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters        {                                        ValidateIssuerSigningKey = true,            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your secret goes here")),            ValidateIssuer = true,            ValidIssuer = "The name of the issuer",            ValidateAudience = true,            ValidAudience = "The name of the audience",            ValidateLifetime = true, //validate the expiration and not before values in the token            ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date        };    });}

如果你不熟悉ASP.NET Core的身份驗證中介軟體,則建議你閱讀External Login Providers in ASP.NET Core。

即使是關於如何使用Google,Facebook等進行外部登陸提供程式登入,但是這篇部落格也含有有關身份驗證中介軟體如何工作的詳細說明。

此外請注意,這是新的ASP.NET Core 2.0語法,其中通過ConfigureServices方法完全配置了身份驗證,但概念是相同的。

譯者注:External Login Providers in ASP.NET Core這篇部落格在撰寫的時候使用的是 Asp.Net Core 1.x。

在這個例子中更重要的是 TokenValidationParameters 類。這是你必須例項化的類,它將用來配置如何驗證token。

在Startup.cs中,你需要更新Configure方法並新增身份驗證中介軟體:

public void Configure(IApplicationBuilder app, IHostingEnvironment env){    //...    app.UseAuthentication(); //needs to be up in the pipeline, before MVC    //...    app.UseMvc(ConfigureRoutes);//..

Client 客戶端

web api客戶端可以是桌面應用程式,移動裝置甚至是瀏覽器。我將要描述的例子是Web應用程式的登入、儲存token、然後使用它來執行對請求的認證。你可以在這裡找到一個可以正常工作的例子。

首先,為了能夠登陸,你需要將使用者名稱和密碼傳送POST請求到“/ token”(或者你設定的獲取token的Web Api斷點)。你可以很容易地使用jQuery來做到這一點:

$.post("/token", $.param({username: "the username", password: "the password"})).done(function(token){    //save the token in local storage    localStorage.setItem("token", token);    //...}).fail(handleError);

如果一切順利,則可以將獲得JWT token,然後你可以將其儲存在某個位置,通常在Web應用程式中,我們將它儲存到 local storage 中。在移動裝置上則取決於你使用的平臺,但它們都具有允許你儲存token的功能(例如Android的SharedPreferences)。

對於上一節中的身份驗證中介軟體,接受JWT token並將其轉換為可以在控制器操作中訪問的User,則該請求必須具有 Authorization header。header的值應該是“Bearer ”,然後是JWT token,例如:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1l...

儘管你可以“手動”將授權標頭新增到每個請求,但通常有自動執行的方法。例如jQuery中有一個時間可以允許你在傳送請求之前做一些操作,例如在這裡檢查是否存在 token,如果有就加到Authentication頭裡。

$.ajaxSetup({    beforeSend: function(xhr) {       
   if (localStorage.getItem("token") !== null) {            xhr.setRequestHeader('Authorization', 'Bearer ' + localStorage.getItem("token"));                              }    }});  

如果你使用其他框架,也有類似的機制,例如Angular有HttpInterceptors。

最後,你只需要從本地儲存中刪除token即可登出:

localStorage.removeItem("token")

需要注意的一件事情是,如果客戶端執行的操作需要使用者進行身份驗證,並且請求中沒有(有效)授權標頭,則伺服器將返回帶有401狀態碼的響應。該響應還將具有WWW-Authenticate:Bearer header。如果你收到這樣的響應,則你可以通知使用者需要驗證身份。


原文:http://www.cnblogs.com/rocketRobin/p/8058760.html



   

.NET社群新聞,深度好文,歡迎訪問公眾號文章彙總 http://www.csharpkit.com

640?wx_fmt=jpeg

                             

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述