D-13-授權 jwt ? authentication ? authorization

無狀態的HttpRequest怎麼做身分驗證

直到昨天將網頁的基礎知識介紹完了,所以今天開始會介紹一些開發上會常會遇到的需求以及如何解決這個需求的套件,所以各位請繼續看下去。

網頁伺服器如何身分驗證

「前輩,我們公司的網站要加上身分驗證的功能,這該怎麼做呢。」
一大早小光就聽到大頭跟老K詢問需求開發的問題,這時他就湊過去想要了解一下他們遇到的問題以及解決的方法。
「恩,不外乎就是使用者輸入帳密後登入,在登入時存放登入的資料,之後再請求是檢查是否有登入了。」
「好喔。」
聽到老K說的建議後大頭轉身並且開始捲起袖子準備大顯身手,這時老K看到他的樣子趕緊阻止他。
「等等...你不會要準備自己搞一套吧。」
這時大頭聽到老K的話之後,用疑惑的眼神看著他。
「來來來,我告訴你這東西已經有人做了,別再自己搞一套了。」
這時老K就跟大頭還有小光介紹如何處理身分驗證的需求。

驗證與授權

基於關注點分離的概念在開發網頁程式的時候要專注地是在商業邏輯的開發上面,因此對於驗證授權這種需求如果可以跟商業邏輯切分出來開發,所以針對dotnetcore對於增加使用者的會把他移至中介軟體來處理就如同驗證與授權所示,接下來細節部分可以看一下下列範例。

首先在Startup.Configure加入以下內容。

.
app.UseAuthentication();  
app.UseAuthorization();  
.

相信大家在中介軟體那部分有看過這兩個東西,這部分是dotnetcore內建的中介軟體,然後這部分要小心中介層軟體的前後順序,所以接下來針對要驗證的Action可以加上Attribute上去如下列範例。

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    .
}

如果要針對特定Action驗證特定Action可以把[Authorize]掛在Action上,如果需要特定權限才可以使用該Action則將該Attribute輸入參數即可,就如同[Authorize(Roles = "Admin")]。接下來再介紹如何在dotnetcore使用jwt Token的方式來進行授權跟認證。

jwt

如果仔細看上述的驗證與授權之後應該已經知道該如何在dotnetcore內使用jwt Token的方式來進行授權跟認證,如果對於看英文內容的文章有困擾的話可以再參考保哥的如何在 ASP.NET Core 3 使用 Token-based 身分驗證與授權 (JWT)這篇文章,而本篇就針對重點式的介紹。

什麼是jwt

在說明怎麼處理之前要先了解一下什麼是jwt,他的全名是JSON Web Token,簡單說明就是他將Token分成三部分。
Header 表頭,說明此Token的類別以及加密方式。
Payload 使用者資料,但不建議放機密內容。除此之外還有逾期時間 
Signature 簽章內容。

之後有沒有經過授權就是這個Token的內容,因為剛有提到逾期時間,所以當時間超過是這個Token就會失效要另外在取得一個Token。接下來當收到此Token時可以不用再跟資料庫查詢就有些資料以存放在Token中,所以可以減少對資料庫的存取。以上就是jwt的內容。所以接下來說明怎麼在dotnetcore內使用jwt。

使用 jwt

這部分說明如何在dotnetcore內使用jwt,這部分可以分成兩步驟,由於認證部分已經是透過Attribute的部分來處理了,所以接下來針對設定Token產生的方式以及如何登入核發Token兩部份來說明。

不過在說明之前請先安裝套件,因為後續很多語法都是透過套件來操作。所以在命列先輸入以下指令。

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Token產生的設定

這部分需要在Startup.ConfigureServices加入以下內容。

// Adding Authentication  
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)  
// Adding Jwt Bearer  
.AddJwtBearer(options =>  
{  
    options.TokenValidationParameters = new TokenValidationParameters()  
    {  
        ValidateIssuer = true,  
        ValidateAudience = true,  
        ValidAudience = Configuration["JWT:ValidAudience"], // 設定檔內Audience資料
        ValidIssuer = Configuration["JWT:ValidIssuer"],  // 設定檔內Issuer資料
        IssuerSigningKey = new SymmetricSecurityKey(Encoding
            .UTF8.GetBytes(Configuration["JWT:Secret"]))  // 簽章密鑰自行設定
    };  
}); 

接下來說明一下如何核發Token。

Token的核發

這部分可以使用保哥那篇文章寫好的Helper來核發即可,這邊也是重點式說明,所以核發的範例如下。

var issuer = "JwtSettings:Issuer"; // 設定檔內Issuer資料
var signKey = "JwtSettings:SignKey"; // 簽章密鑰自行設定
// 設定要加入到 JWT Token 中的聲明資訊(Claims)
var claims = new List<Claim>();

claims.Add(new Claim(JwtRegisteredClaimNames.Sub, userName)); // User.Identity.Name
claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())); // JWT ID

// 你可以自行擴充 "roles" 加入登入者該有的角色
claims.Add(new Claim("roles", "Admin"));
claims.Add(new Claim("roles", "Users"));
var userClaimsIdentity = new ClaimsIdentity(claims);
// 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signKey));
// HmacSha256 有要求必須要大於 128 bits,所以 key 不能太短,至少要 16 字元以上
// https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
// 建立 SecurityTokenDescriptor
var tokenDescriptor = new SecurityTokenDescriptor
{
    Issuer = issuer,
    Subject = userClaimsIdentity,
    Expires = DateTime.Now.AddMinutes(expireMinutes),
    SigningCredentials = signingCredentials
};
// 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var serializeToken = tokenHandler.WriteToken(securityToken);
return serializeToken;

所以經過這方法可以得到一組string的Token,所以在這之前可以先驗證資料庫帳密是否符合,並且可以由資料庫中取的權限的資料於上述例子中放進去Token之中,最後再將這組Token返還給使用者保存即可。

Token的使用

前面說明過如何在伺服器端驗證請求是否經過授權,所以這邊先說明在客戶端如何使用Token,簡單說明就是在發出請求前先把Token放在請求的Header內即可,放Token的方式如下。

Authorization: Bearer eyJhbGci...<snip>...yu5CSpyHI

而伺服器端當收到Token可以透過下列方式取得存放在Token內的資料。

User.Identity.Name; // 使用者名稱
User.Claims; // 其他存放在Token的資訊

後記

經過今天的內容跟大家介紹如何在網頁程式中加入身分驗證,以及介紹如何使用jwt token來處理授權問題,希望能夠幫助大家專心在本質商業邏輯的開發。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *