首頁

目前文章總數:157 篇

  

最後更新:2024年 12月 07日

0042. .Net Core 如何在 WebAPI 中使用 JWT 並實現自動驗證

日期:2023年 07月 01日

標籤: C# Asp.net Core Web MVC Web JSON Web Tokens(JWT) Swagger WebAPI

摘要:C# 學習筆記


應用所需:1. Visual Studio 2022
     2. .net core Web專案 (Web Api示範)
範例檔案:連結
解決問題:1. WebAPI專案使用Swagger攜帶JWT對API操作
     2. API街口如何自動驗證JWT
應用層面:
基本介紹:本篇分為3大部分。
第一部分:WebAPI專案準備
第二部分:範例專案說明
第三部分:Demo展示結果






第一部分:WebAPI專案準備

Step 1:建立專案

建立 Asp.Net Core WebAPI 專案,請使用C# 6.0以上版本


Step 2:Swagger預設配置

在.net core中,會預設配置Swagger(),在Development環境下時會自動啟用


Step 3:安裝套件 JwtBearer-1

我們預期在Swagger中可以使用 Jwt,所以要在Nuget安裝該套件


Step 4:安裝套件 JwtBearer-2

選擇JwtBearer安裝即可。




第二部分:範例專案說明

Step 1:範例專案架構


1. 網站建構 網站程序初始化的入口點,這邊調整Swagger配置,使期可配置JWT
2. 資源配置 JWT的憑證、發行者、使用者密鑰配置
3. WebAPI API的配置內容、範例實現登入、登入後取用戶資訊
4. JWT產生器 產生Json Web Token的方法




Step 2:網站建構

網站建構主要完成4個部分,如下說明

1. 增加右上角Bearer token 使用Swagger時增加右上角的按鈕
2. 建立JwtBearer 我們使用JwtBearer套件,當呼叫API時會自動驗證JWT
3. 註冊 Json Web Token產生器 依賴註冊Service
4. 添加驗證 讓Attribute的Authorize標題啟用,使API會進行驗證


代碼可參考如下:


var builder = WebApplication.CreateBuilder(args);

\\配置其他Service...

// 1. 增加右上角Bearer token
builder.Services.AddSwaggerGen(o =>
{
    o.SwaggerDoc("v1", new OpenApiInfo()
    {
        Title = "JWT Authentication",
        Description = "API如果不是[AllowAnonymous] 需要先執行Member/Login 取得JWT Token",
    });
    o.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
    {
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "驗證使用方式:Authentication輸入。 格式:[Bearer 您的Token]",
    });
    o.AddSecurityRequirement(new OpenApiSecurityRequirement() {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new string[] {}
        }});
});

//2-1. 建立JwtBearer
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
    //2-2. 配置:驗證錯誤時是否顯示錯誤資訊
    options.IncludeErrorDetails = true;
    //2-3. 配置:Swagger的Bearer的自動驗證
    options.TokenValidationParameters = new TokenValidationParameters
    {
        // 2-4. 配置:驗證 Issuer
        ValidateIssuer = true,
        ValidIssuer = builder.Configuration.GetValue<string>("JwtSettings:Issuer"),

        // 2-5. 配置:驗證 Audience
        ValidateAudience = false,
        ValidAudience = builder.Configuration.GetValue<string>("JwtSettings:Audience"), // 不驗證就不需要填寫

        // 2-6. 配置:驗證 Token憑證
        ValidateIssuerSigningKey = false,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetValue<string>("JwtSettings:SignKey"))),

        // 2-7. 配置:驗證 Token 有效時限
        ValidateLifetime = true
    };
});

//3. 註冊 Json Web Token產生器
builder.Services.AddScoped<IJsonWebTokenService, JsonWebTokenService>();

var app = builder.Build();

\\配置其他app configure...

//4. 添加驗證,Attribute => [Authorize]
app.UseAuthorization();
app.Run();



Step 3:資源配置

添加產生Token所需的相關金鑰,這邊用到使用者、發行者、憑證


{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  //1. 添加JWT的相關密鑰
  "JwtSettings": {
    "Issuer": "JwtLoginIssuer",
    "Audience ": "JwtLoginAudience",
    "SignKey": "this_is_a_secure_key_with_length_greater_than_32"
  },
  "AllowedHosts": "*"
}



Step 4:WebAPI

這邊分成三個部分

1. 實現登入API 添加 [AllowAnonymous] 標頭表示可以任意訪問,如果有輸入帳號密碼取得Token
2. 使用者訊息API 添加 [Authorize] 會經過驗證JWT,通過後才可以取得內容否則,回傳401(驗證失敗)
3. 註冊 JwtBearer 套件 這邊宣告 [Authorize] 會使用 JwtBearerDefaults 的套件,使有掛載的API,都會經過JWT自動驗證

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace NetCoreSwaggerJWTExample.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    //3. 這邊Authorize改用 JwtBearer 套件
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    public class MemberController : ControllerBase
    {
        private readonly IJsonWebTokenService _jwtService;

        public MemberController(IJsonWebTokenService jsonWebToken) 
        {
            _jwtService = jsonWebToken;
        }

        /// <summary>        
        /// 1-1. 登入
        /// </summary>        
        [HttpPost]        
        [AllowAnonymous]
        public ActionResult Login(string user, string password)
        {
            string token = string.Empty;
            //1-2. 假設登入成功,回傳Token
            if (!string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(password))
            {
                token = _jwtService.GenerateToken(user);
            }
            
            return Ok(token);
        }

        /// <summary>
        /// 2-1. 使用者訊息API
        /// </summary>        
        [HttpGet]
        [Authorize]
        public ActionResult UserInfo()
        {          
            //2-2. 驗證通過可以取得使用者訊息
            return Ok("取得用戶資訊成功");
        }


    }
}




Step 5:JWT產生器

產生TOKEN的方法,可以參閱0040. .Net Core 使用Json Web Tokne(JWT)實現網站登入、登出功能 - 基本登入、登出</a>
這邊不同的是從 appsettings.json 取得金鑰


public class JsonWebTokenService : IJsonWebTokenService
{
    private readonly string _secretKey;
    private readonly string _issur;
    private readonly string _audience;
    public JsonWebTokenService(IConfiguration configuration)
    {
        _secretKey = configuration.GetValue<string>("JwtSettings:SignKey");
        _issur = configuration.GetValue<string>("JwtSettings:Issuer");
        _audience = configuration.GetValue<string>("JwtSettings:Audience");
    }
    //1. 產生Token
    public string GenerateToken(string user)
    {
        var claims = new[] {
            new Claim(ClaimTypes.Name, user)                
        };
        var jwtKey = Encoding.UTF8.GetBytes(_secretKey);
        var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(jwtKey), SecurityAlgorithms.HmacSha256);
        //2-1. Token產生的issuer、audience、signingCredentials 要與JWTBearer 的配置相同
        var token = new JwtSecurityToken(
            issuer: _issur,    
            audience: _audience, 
            signingCredentials: signingCredentials,
            claims: claims,                    
            expires: DateTime.UtcNow.AddSeconds(10)
        );
        //2-2. 轉為String回傳
        var newToken = new JwtSecurityTokenHandler().WriteToken(token);
        return newToken;
    }
}





第三部分:Demo展示結果

Step 1:執行專案

執行Debug模式,可以看到預設帶到Swagger首頁,有2個API
1. Login
2. UserInfo





Step 2:沒帶Token - 1

先關注到API UserInfo,我們點擊Try it out



Step 3:沒帶Token - 2

執行Execute後,可以看到下面黑框 Response Stauts 401 ,沒有經過驗證



Step 4:沒帶Token - 2

執行Execute後,可以看到下面黑框 Response Stauts 401 ,沒有經過驗證



Step 5:登入取得Token - 1

關注到API Login 進行登入,這邊只是測試,隨意輸入帳號密碼取得Token



Step 6:登入取得Token - 2

成功登入後,將取得的Json Web Token複製,我們要將其貼到Swagger的驗證中,使API可以訪問



Step 7:Swagger驗證 - 1

1.回到頁面最上方,點擊右上角的Authorize
2.將Token貼上到Value的位置,一定要在前面加上 Bearer 後面有一個空白字元,如下:

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiMTIzIiwiZXhwIjoxNjg4MDAzODM2LCJpc3MiOiJKd3RMb2dpbklzc3VlciJ9.6fmvcTd_gN2kNFA5wknZIx-vXi0lUS3gBM7AhFcUd1Y





Step 8:Swagger驗證 - 2

執行Authorize,完成



Step 9:訪問驗證API - 成功

這時再次執行API UserInfo,可以正常訪問了



Step 10:補充說明

如果隔一段時間,再次執行API UserInfo,會發現又出現 401,這是正常的,因為我們在JWT中有設定令牌秒數。
※一個合理的JWT時間應視系統而決定,如果太長會有被攻擊的風險,太短則會需頻繁的產生Token


var token = new JwtSecurityToken(
    issuer: _issur,    
    audience: _audience, 
    signingCredentials: signingCredentials,
    claims: claims,                    
    expires: DateTime.UtcNow.AddSeconds(10)//超過10秒就超時
);