在 ASP.NET Core WebAPI 中使用 JWT 驗證

栏目: ASP.NET · 发布时间: 5年前

内容简介:為了保護 WebAPI 僅提供合法的使用者存取,有很多機制可以做,透過 JWT (JSON Web Token) 便是其中一種方式,這篇示範如何使用官方所提供的順道一提,要產生 JWT Token 有很多套件可以幫助開發者快速建立,使用 Visual Studio 2019 建立 ASP.NET Core WebAPI 專案後,首先修改

為了保護 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();
}

如此一來網站的基本設定就搞定了。

取得 JWT Token

要如何產生 JWT Token 呢?這裡我們建立了一個 AuthController 控制器來產生所需要的 JWT Token,流程如下:

  1. 身分驗證
  2. 建立使用者聲明資訊
  3. 取得加密金鑰
  4. 建立 JWT Token

身分驗證是你的需求自行實作,驗證後可將取得的使用者資訊(非機敏資訊)包進使用者的 Claim 聲明中,這些資訊將會是 JWT Payload 的一部分,這裡的 Claim 聲明資訊也可以根據你的需求客制增加。

我們知道 JWT 是用三部分 Header、Payload 和 Signature,並使用 (.)將三個部分連結起來成為一個字串,Signature 這部分會是 Header、Payload 加上一組 Secret 做雜湊運算產生出來的,用來驗證整個 JWT 資訊是沒有被竄改過。加密金鑰這段雖然是選用,但還是相當建議加上去,增加安全強度。

接著透過 System.IdentityModel.Tokens.Jwt 這個命名空間底下的 JwtSecurityTokenHandler 來產生 JWT Token,而 JWT Token 的內容描述則交由 SecurityTokenDescriptor 來組合,在 JWT Token 的內容描述中,請根據需求做調整。

如此一來就可以產生所需要的 JWT Token 了。

[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _config;

    public AuthController(IConfiguration configuration)
    {
        _config = configuration;
    }

    // GET api/auth/login
    [HttpGet, Route("login")]
    public IActionResult Login(string name)
    {
        // STEP0: 在產生 JWT Token 之前,可以依需求做身分驗證

        // STEP1: 建立使用者的 Claims 聲明,這會是 JWT Payload 的一部分
        var userClaims = new ClaimsIdentity(new[] {
            new Claim(JwtRegisteredClaimNames.NameId, name),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim("CustomClaim", "Anything You Like")
        });
        // STEP2: 取得對稱式加密 JWT Signature 的金鑰
        // 這部分是選用,但此範例在 Startup.cs 中有設定 ValidateIssuerSigningKey = true 所以這裡必填
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        // STEP3: 建立 JWT TokenHandler 以及用於描述 JWT 的 TokenDescriptor
        var tokenHandler = new JwtSecurityTokenHandler();
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Issuer = _config["Jwt:Issuer"],
            Audience = _config["Jwt:Issuer"],
            Subject = userClaims,
            Expires = DateTime.Now.AddMinutes(30),
            SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
        };
        // 產出所需要的 JWT Token 物件
        var securityToken = tokenHandler.CreateToken(tokenDescriptor);
        // 產出序列化的 JWT Token 字串
        var serializeToken = tokenHandler.WriteToken(securityToken);

        return new ContentResult() { Content = serializeToken };
    }
}

如何使用

使用上非常簡單,只要掛上需要的裝飾器即可,這裡建立了 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 專案。

參考資料:


以上所述就是小编给大家介绍的《在 ASP.NET Core WebAPI 中使用 JWT 驗證》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Essential C++中文版

Essential C++中文版

李普曼 (Stanley B.Lippman) / 侯捷 / 电子工业出版社 / 2013-8-1 / CNY 65.00

本书以四个面向来表现C++的本质:procedural(面向过程的)、generic(泛型的)、object-based(基于对象的)、objectoriented(面向对象的)。全书围绕一系列逐渐繁复的程序问题,以及用以解决这些问题的语言特性来组织。循此方式,你将不只学到C++的功能和结构,也可学到它们的设计目的和基本原理。 本书适合那些已经开始从事软件设计,又抽不出太多时间学习新技术的程......一起来看看 《Essential C++中文版》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

SHA 加密
SHA 加密

SHA 加密工具