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来处理授权问题,希望能够帮助大家专心在本质商业逻辑的开发。


<<:  Day 17: 人工智慧在音乐领域的应用 (AI作曲-基因演算法一)

>>:  [Day17] 注册API – 测试阶段之输入基本资料

Day 18 「春暖鸭先知」TDD 来了

古语有云:「竹外桃花三两枝,春江水暖鸭先知。」春天不会早上起来敲你家门,跟你说他来了。冬天进入春天的...

DAY10:验证码辨识(三)

今天要用昨天训练好的模型来试试看能否顺利从我们的目标网站取得资讯! 我们要先用selenium来处理...

Day24 ( 高级 ) 骇客任务背景特效

骇客任务背景特效 教学原文参考:骇客任务背景特效 这篇文章会介绍,如何在 Scratch 3 里使用...

Flutter基础介绍与实作-Day26 旅游笔记的实作(7)

到昨天为止我们终於把美食的部分都做完了,再来要做的是美景的部分,其实做法跟前面的都差不多,我这边就会...

[Day13] JavaScript 的原始型别

JavaScript 的型别分为两大类,原始型别 与 物件型别,而原始型别有 7 种,其他则是物件型...