在 ASP.NET Core WebAPI 中使用 JWT 验证

为了保护 WebAPI 仅提供合法的使用者存取,有很多机制可以做,透过 JWT (JSON Web Token) 便是其中一种方式,这篇示范如何使用官方所提供的 System.IdentityModel.Tokens.Jwt 扩充套件,处理呼叫 API 的来源是否为合法的使用者身分。

顺道一提,要产生 JWT Token 有很多套件可以帮助开发者快速建立,JWT 这个 NuGet 套件就是其中一个,但这裡我使用官方所提供的 System.IdentityModel.Tokens.Jwt 扩充套件来处理,虽然这是官方提供的版本,但写起来一点也不困难。.

建立专案

使用 Visual Studio 2019 建立 ASP.NET Core WebAPI 专案后,首先修改 Startup.cs 中的 ConfigureServices 方法,设定这个 WebAPI 站台要使用哪种方式来验证 HTTP Request 是否合法,程式码如下:

public void ConfigureServices(IServiceCollection services){    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);    // STEP1: 设定用哪种方式验证 HTTP Request 是否合法    services        // 检查 HTTP Header 的 Authorization 是否有 JWT Bearer Token        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)        // 设定 JWT Bearer Token 的检查选项        .AddJwtBearer(options =>        {            options.TokenValidationParameters = new TokenValidationParameters            {                ValidateIssuer = true,                ValidIssuer = Configuration["Jwt:Issuer"],                ValidateAudience = true,                ValidAudience = Configuration["Jwt:Issuer"],                ValidateLifetime = true,                ValidateIssuerSigningKey = true,                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))            };        });}

这裡我们设定系统在验证 JWT Token 时,必须要符合以下 4 个条件:

  1. 相同的 Issuer 设定值

  2. 相同的 Audience 设定值

  3. 验证 Token 有效期限

  4. 符合对称式加密的签章

接者一样在 Startup.cs 中的 Configure 加入验证权限用的 Middleware,让每次进来的 HTTP Request 都会经过此层验证机制,程式码如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env){    // 略...    // STEP2: 使用验证权限的 Middleware    app.UseAuthentication();    app.UseHttpsRedirection();    app.UseMvc();}

如何使用

使用上非常简单,只要挂上需要的装饰器即可,这裡建立了 ValuesController 做测试,分别挂上 [AllowAnonymous] 和 [Authorize],前者是给匿名登入使用,也就是不需要有 JWT Token 也能执行,后者则必须要在 HTTP 的 Authorization Header 必须设定合法的 JWT Bearer Token 才能使用。

[Route("api/[controller]")][ApiController]public class ValuesController : ControllerBase{    // GET api/values/anonymous    /// <summary>使用匿名登入,无视于身分验证</summary>    [AllowAnonymous]    [HttpGet, Route("anonymous")]    public IActionResult Anonymous()    {        return new ContentResult() { Content = $@"For all anonymous." };    }    // GET api/values/authorize    /// <summary>使用身分验证,HTTP 的 Authorization Header 必须设定合法的 JWT Bearer Token 才能使用</summary>    [Authorize]    [HttpGet, Route("authorize")]    public IActionResult All()    {        return new ContentResult() { Content = $@"For all client who authorize." };    }}

这个验证装饰器还可以有很多种玩法,例如根据所建立的验证 Policy 做验证,或根据使用者 Claim 声明的角色做验证,提供了很大的弹性来处理。

JwtRegisteredClaimNames 属性说明

在建立使用者的 Claims 声明时,我们会用到很多 JwtRegisteredClaimNames 结构型别,来取得是先定义好的字串,在 System.IdentityModel.Tokens.Jwt 命名空间中的 JwtRegisteredClaimNames 定义了很多 JWT 会用到的声明,但官方文件说明相当的少,自行整理了如下:

声明栏位 说明 连结
Jti 表示 JWT ID,Token 的唯一识别码 http://tools.ietf.org/html/rfc7519#section-4
Iss 表示 Issuer,发送 Token 的发行者 http://tools.ietf.org/html/rfc7519#section-4
Iat 表示 Issued At,Token 的建立时间 http://tools.ietf.org/html/rfc7519#section-4
Exp 表示 Expiration Time,Token 的逾期时间 http://tools.ietf.org/html/rfc7519#section-4
Sub 表示 Subject,Token 的主体内容 http://tools.ietf.org/html/rfc7519#section-4
Aud 表示 Audience,接收 Token 的观众 http://tools.ietf.org/html/rfc7519#section-4
Typ 表示 Token 的类型,例如 JWT 表示 JSON Web Token 类型 http://tools.ietf.org/html/rfc7519#section-4
Nbf 表示 Not Before,定义在什麽时间之前,不可用 http://tools.ietf.org/html/rfc7519#section-4
Actort 识别执行授权的代理是谁 http://tools.ietf.org/html/rfc7519#section-4
Prn   http://tools.ietf.org/html/rfc7519#section-4
Nonce   http://tools.ietf.org/html/rfc7519#section-4
NameId 使用者识别码 http://tools.ietf.org/html/rfc7519#section-4
FamilyName 使用者姓氏 http://tools.ietf.org/html/rfc7519#section-4
GivenName 使用者名字 http://tools.ietf.org/html/rfc7519#section-4
Gender 使用者性别 http://tools.ietf.org/html/rfc7519#section-4
Email 使用者的电子邮件 http://tools.ietf.org/html/rfc7519#section-4
Birthdate 使用者生日 http://tools.ietf.org/html/rfc7519#section-4
Website 使用者的网站 http://tools.ietf.org/html/rfc7519#section-4
CHash   http://tools.ietf.org/html/rfc7519#section-4
UniqueName   http://tools.ietf.org/html/rfc7519#section-4
AtHash   http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
Acr   http://openid.net/specs/openid-connect-core-1_0.html#IDToken
Amr   http://openid.net/specs/openid-connect-core-1_0.html#IDToken
Azp   http://openid.net/specs/openid-connect-core-1_0.html#IDToken
AuthTime   http://openid.net/specs/openid-connect-core-1_0.html#IDToken
Sid   http://openid.net/specs/openid-connect-frontchannel-1_0.html#OPLogout

有些定义的声明栏位很难找到说明,有找到相关资讯再陆续补充。

程式码

关于本篇文章完整的程式码发布于 GitHub:poychang/Demo-WebAPI-Jwt-Auth,请参考裡面的 SimpleJwtAuth 专案。