Google Code Prettify

顯示具有 JWT 標籤的文章。 顯示所有文章
顯示具有 JWT 標籤的文章。 顯示所有文章

JWT 作為身份驗證要如何作登入、登出及重新驗證?

使用 JWT(JSON Web Token)作為身份驗證的系統中,登錄、登出和重新驗證的實現涉及生
成、驗證和管理令牌。以下是實現這些功能的詳細步驟。

登錄
登錄過程包括驗證用戶憑證並生成一個 JWT,該 JWT 會包含用戶身份信息和其他聲明(claims)。

1. 建立登入端點
創建一個登入端點,該端點接受用戶憑證(例如,電子郵件和密碼)並返回一個 JWT。

csharp

[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest loginRequest)
{
    // 1. 驗證用戶憑證
    var user = _userService.ValidateUser(loginRequest.Email, loginRequest.Password);
    if (user == null)
    {
        return Unauthorized();
    }

    // 2. 生成 JWT
    var token = GenerateJwtToken(user);

    // 3. 返回 JWT
    return Ok(new { Token = token });
}

private string GenerateJwtToken(User user)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.ASCII.GetBytes("Your_Secret_Key");
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new Claim[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.Username)
        }),
        Expires = DateTime.UtcNow.AddHours(1),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
        Issuer = "your_issuer",
        Audience = "your_audience"
    };
    var token = tokenHandler.CreateToken(tokenDescriptor);
    return tokenHandler.WriteToken(token);
}

2. 定義登入請求模型
csharp

public class LoginRequest
{
    public string Email { get; set; }
    public string Password { get; set; }
}

登出
由於 JWT 是無狀態的,不需要服務器端存儲,因此登出通常是前端的操作。登出只需要在前端刪除 JWT。

前端處理登出
javascript

function logout() {
    localStorage.removeItem("jwtToken");
    window.location.href = "/login";
}



重新驗證
重新驗證(令牌刷新)需要提供一個新的 JWT,這通常涉及到一個刷新令牌。以下是實現令牌刷新的步驟。

1. 創建刷新令牌
每當用戶登錄時,除了返回 JWT,還返回一個刷新令牌。

csharp

[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest loginRequest)
{
    var user = _userService.ValidateUser(loginRequest.Email, loginRequest.Password);
    if (user == null)
    {
        return Unauthorized();
    }

    var token = GenerateJwtToken(user);
    var refreshToken = GenerateRefreshToken();

    // 將刷新令牌保存到數據庫或內存中,關聯用戶
    _userService.SaveRefreshToken(user.Id, refreshToken);

    return Ok(new { Token = token, RefreshToken = refreshToken });
}

private string GenerateRefreshToken()
{
    var randomNumber = new byte[32];
    using (var rng = RandomNumberGenerator.Create())
    {
        rng.GetBytes(randomNumber);
        return Convert.ToBase64String(randomNumber);
    }
}

2. 創建刷新令牌請求端點
csharp

[HttpPost("refresh")]
public IActionResult Refresh([FromBody] RefreshRequest refreshRequest)
{
    var principal = GetPrincipalFromExpiredToken(refreshRequest.Token);
    var userId = principal.FindFirst(ClaimTypes.NameIdentifier).Value;
    
    var savedRefreshToken = _userService.GetRefreshToken(userId); // 從數據庫中獲取保存的刷新令牌

    if (savedRefreshToken != refreshRequest.RefreshToken)
    {
        return Unauthorized();
    }

    var newJwtToken = GenerateJwtToken(principal);
    var newRefreshToken = GenerateRefreshToken();
    
    _userService.SaveRefreshToken(userId, newRefreshToken); // 更新刷新令牌

    return Ok(new { Token = newJwtToken, RefreshToken = newRefreshToken });
}

private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
{
    var tokenValidationParameters = new TokenValidationParameters
    {
        ValidateAudience = false,
        ValidateIssuer = false,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Your_Secret_Key")),
        ValidateLifetime = false // 我們正在驗證過期的令牌,因此這裡不驗證過期時間
    };

    var tokenHandler = new JwtSecurityTokenHandler();
    var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken securityToken);
    var jwtSecurityToken = securityToken as JwtSecurityToken;

    if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
    {
        throw new SecurityTokenException("Invalid token");
    }

    return principal;
}

3. 定義刷新請求模型
csharp

public class RefreshRequest
{
    public string Token { get; set; }
    public string RefreshToken { get; set; }
}

總結
這樣的設計使得 JWT 可以用於用戶身份驗證,並且支持登錄、登出和重新驗證功能。JWT 的優點在於
其無狀態性,適合分布式系統中的身份驗證,但同時也需要考慮安全性問題,例如令牌過期時間、刷新
令牌的安全管理等。

JWT 的 Issuer 什麼作用?

JWT (JSON Web Token) 的 issuer (簡稱為 iss) 是 JWT 標頭和有效載荷中的一部分,用於指
示 JWT 的發行者。這是一個可選的聲明,但在很多情況下它被用來幫助驗證和識別令牌的來源。以
下是 issuer 的主要作用和設置方式:


主要作用
標識發行者:iss 聲明用來標識誰發行了這個令牌。這可以是發行令牌的認證伺服器的 URL 或名稱。
驗證令牌來源:在驗證令牌時,服務可以檢查令牌的 iss 聲明,以確保令牌是從可信任的發行者那裡
來的,從而防止來自未知或不受信任的來源的攻擊。

實現多租戶支持:在多租戶系統中,不同租戶可以有不同的發行者。通過檢查 iss,系統可以識別和
分離不同租戶的令牌。

設置 issuer
在生成 JWT 時,你需要設置 issuer。例如,如果你使用 System.IdentityModel.Tokens.Jwt 
庫來生成 JWT,設定 issuer 的方式如下:


csharp

var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

var tokenDescriptor = new SecurityTokenDescriptor
{
    Subject = new ClaimsIdentity(new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, "user_id"),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
    }),
    Expires = DateTime.UtcNow.AddHours(1),
    Issuer = "your_issuer", // 設定發行者
    Audience = "your_audience",
    SigningCredentials = credentials
};

var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var jwtToken = tokenHandler.WriteToken(token);

Console.WriteLine(jwtToken);

驗證 issuer
在驗證 JWT 時,你也需要檢查 issuer。這可以通過設定 TokenValidationParameters 來實現:

csharp

var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
    ValidateIssuer = true, // 啟用發行者驗證
    ValidIssuer = "your_issuer", // 預期的發行者
    ValidateAudience = true,
    ValidAudience = "your_audience",
    ValidateLifetime = true,
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key"))
};

try
{
    var principal = tokenHandler.ValidateToken(jwtToken, validationParameters, out SecurityToken validatedToken);
    // Token 驗證成功,principal 包含了用戶的身份信息
}
catch (SecurityTokenException ex)
{
    // Token 驗證失敗
    Console.WriteLine(ex.Message);
}


總結
JWT 中的 issuer (iss) 聲明用來標識令牌的發行者,並在令牌驗證過程中幫助確認令牌的來
源是否合法和可信。設置和驗證 issuer 是確保 JWT 安全性的重要步驟,特別是在多租戶環境
和需要高安全性的應用中。

存取的 RESTful API 使用 JWT 作為身份驗證方式,出現 401 Unauthorized 錯誤

出現 401 Unauthorized 錯誤通常表示在授權過程中出現問題。以下是一些常見的原因以及解決方案:

   JWT 沒有正確附加到請求標頭:確認 JWT 被正確地從 Session 中獲取並附加到請求的授權標頭中。
   API URL 錯誤或過期:確認 API 的 URL 正確並且沒有過期。
   JWT 過期:確認 JWT 沒有過期,如果過期需要重新獲取。
   CORS 問題:如果你從不同的域名訪問 API,確保 API 設置了正確的 CORS 政策。
   API 配置問題:確保 API 正確配置了身份驗證和授權策略。


以下是一些診斷和解決這些問題的步驟:

檢查 JWT 是否正確附加到請求標頭
確保 AuthorizedHttpClientHandler 中的 JWT 確實被附加到請求標頭中。你可以添加一些調試輸出來確認這一點:

csharp

public class AuthorizedHttpClientHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = _httpContextAccessor.HttpContext.Session.GetString("JWT");
        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
        else
        {
            // 記錄沒有獲取到 Token 的情況
            Console.WriteLine("No JWT token found in session.");
        }

        // 記錄請求詳細信息
        Console.WriteLine($"Request: {request.Method} {request.RequestUri}");
        Console.WriteLine($"Authorization: {request.Headers.Authorization}");

        return await base.SendAsync(request, cancellationToken);
    }
}

檢查 API URL 和配置
確保你使用的是正確的 API URL,並且 API 正確配置了身份驗證和授權策略。

確認 JWT 沒有過期
如果 JWT 過期,你需要重新獲取新的 JWT。你可以在 API 的響應中檢查是否返回了過期錯誤信息。

配置 CORS
如果你從不同的域名訪問 API,請確保 API 設置了正確的 CORS 政策。例如:

csharp

// 在 API 的 Startup.cs 中配置 CORS
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("AllowSpecificOrigin",
            builder => builder.WithOrigins("https://yourclientapp.com")
                              .AllowAnyHeader()
                              .AllowAnyMethod());
    });

    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseCors("AllowSpecificOrigin");

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
驗證整個流程
確保整個流程沒有問題,從登入到 API 請求。


完整示例
以下是更新後的完整示例:

csharp

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("AuthorizedClient")
        .AddHttpMessageHandler<AuthorizedHttpClientHandler>();

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddTransient<AuthorizedHttpClientHandler>();

    services.AddControllersWithViews();
    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(30);
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

// AccountController.cs
public class AccountController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public AccountController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public IActionResult Login()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Login(LoginViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var client = _httpClientFactory.CreateClient();
        var loginUrl = "https://yourapi.com/api/auth/login";
        var loginData = new
        {
            Username = model.Username,
            Password = model.Password
        };

        var content = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json");
        var response = await client.PostAsync(loginUrl, content);

        if (response.IsSuccessStatusCode)
        {
            var responseString = await response.Content.ReadAsStringAsync();
            var jwt = JsonSerializer.Deserialize<JwtResponse>(responseString);

            // 存儲 JWT 到 Session
            HttpContext.Session.SetString("JWT", jwt.Token);

            return RedirectToAction("Index", "Home");
        }

        ModelState.AddModelError(string.Empty, "Invalid login attempt.");
        return View(model);
    }
}

// AuthorizedHttpClientHandler.cs
public class AuthorizedHttpClientHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = _httpContextAccessor.HttpContext.Session.GetString("JWT");
        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
        else
        {
            Console.WriteLine("No JWT token found in session.");
        }

        Console.WriteLine($"Request: {request.Method} {request.RequestUri}");
        Console.WriteLine($"Authorization: {request.Headers.Authorization}");

        return await base.SendAsync(request, cancellationToken);
    }
}

// SomeController.cs
public class SomeController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public SomeController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<IActionResult> SomeAction()
    {
        var client = _httpClientFactory.CreateClient("AuthorizedClient");
        var response = await client.GetAsync("https://yourapi.com/api/protectedresource");

        if (response.IsSuccessStatusCode)
        {
            var data = await response.Content.ReadAsStringAsync();
            // 處理數據
        }
        else
        {
            // 處理錯誤
        }

        return View();
    }
}
總結
這些步驟應該可以幫助你診斷和解決 401 Unauthorized 錯誤。請確保 JWT 被正確地附加到每個需要授權的請求中,並且 API 端配置正確。

在 ASP.NET Core MVC 應用中實現使用 JWT 的登入功能並將 JWT token 保存到 Session 中

在 ASP.NET Core MVC 應用中實現使用 JWT 的登入功能並將 JWT token 保存到 Session 中,涉及以下步驟:

創建登入頁面:用戶輸入他們的憑證(如用戶名和密碼)。
發送登入請求:從 MVC 應用發送一個請求到 API 來獲取 JWT。
存儲 JWT:將 JWT 存儲在 Session 中。
設置授權標頭:在後續請求中使用 JWT 進行身份驗證。



步驟 1:創建登入頁面
首先,在你的 MVC 應用中創建一個簡單的登入頁面,讓用戶輸入用戶名和密碼。

html

<!-- Views/Account/Login.cshtml -->
@{
    ViewData["Title"] = "Login";
}

<h2>@ViewData["Title"]</h2>

<form asp-action="Login" method="post">
    <div>
        <label asp-for="Username"></label>
        <input asp-for="Username" />
    </div>
    <div>
        <label asp-for="Password"></label>
        <input asp-for="Password" type="password" />
    </div>
    <button type="submit">Login</button>
</form>



步驟 2:創建登入模型
在 MVC 應用中創建一個模型來綁定登入頁面的輸入。

csharp

// Models/LoginViewModel.cs
using System.ComponentModel.DataAnnotations;

public class LoginViewModel
{
    [Required]
    public string Username { get; set; }

    [Required]
    public string Password { get; set; }
}



步驟 3:創建控制器來處理登入請求
創建一個控制器來處理登入邏輯,包括發送請求到 API 並獲取 JWT,然後將 JWT 存儲在 Session 中。

csharp

// Controllers/AccountController.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using YourNamespace.Models;

public class AccountController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public AccountController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public IActionResult Login()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Login(LoginViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var client = _httpClientFactory.CreateClient();
        var loginUrl = "https://yourapi.com/api/auth/login";
        var loginData = new
        {
            Username = model.Username,
            Password = model.Password
        };

        var content = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json");
        var response = await client.PostAsync(loginUrl, content);

        if (response.IsSuccessStatusCode)
        {
            var responseString = await response.Content.ReadAsStringAsync();
            var jwt = JsonSerializer.Deserialize<JwtResponse>(responseString);

            // 存儲 JWT 到 Session
            HttpContext.Session.SetString("JWT", jwt.Token);

            return RedirectToAction("Index", "Home");
        }

        ModelState.AddModelError(string.Empty, "Invalid login attempt.");
        return View(model);
    }
}

public class JwtResponse
{
    public string Token { get; set; }
}



步驟 4:配置 Session
在 Startup.cs 中配置 Session。

csharp

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient();
    services.AddControllersWithViews();
    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(30); // 可以根據需求調整
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseSession(); // 必須在 UseRouting 和 UseAuthentication 之間
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}



步驟 5:設置授權標頭
在每次發送 HTTP 請求時,從 Session 中獲取 JWT 並設置授權標頭。

csharp

public class AuthorizedHttpClientHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = _httpContextAccessor.HttpContext.Session.GetString("JWT");
        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("AuthorizedClient")
        .AddHttpMessageHandler<AuthorizedHttpClientHandler>();

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddTransient<AuthorizedHttpClientHandler>();

    services.AddControllersWithViews();
    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(30);
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
    });
}

使用 AuthorizedHttpClient 發送請求
在需要授權的請求中使用 AuthorizedHttpClient。

csharp

public class SomeController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public SomeController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<IActionResult> SomeAction()
    {
        var client = _httpClientFactory.CreateClient("AuthorizedClient");
        var response = await client.GetAsync("https://yourapi.com/api/protectedresource");

        if (response.IsSuccessStatusCode)
        {
            var data = await response.Content.ReadAsStringAsync();
            // 處理數據
        }
        else
        {
            // 處理錯誤
        }

        return View();
    }
}



將這些部分組合起來,你將擁有一個完整的 MVC 應用,允許用戶登入並將 JWT 存儲在 Session 中,從而在後續的請求中使用這個 JWT 進行身份驗證。

這種方式不僅實現了基於 JWT 的身份驗證,還充分利用了 ASP.NET Core 的 Session 管理功能來安全地存儲和使用 JWT。

使用JWT時,如何增加Policy?

 在 ASP.NET Core 8 中,授權策略(Policy)提供了一種靈活的方法來控制應用程式中的授權需求。你可以定義自訂的授權策略,並在控制器或動作方法中應用這些策略。以下是增加和使用授權策略的步驟:

步驟 1:設定授權策略

Program.cs 中,使用 AddAuthorization 方法來配置授權策略。例如,你可以定義一個基於角色的策略和一個基於自訂要求的策略。

var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "yourissuer", ValidAudience = "youraudience", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("yoursecretkey")) }; }); builder.Services.AddAuthorization(options => { // 基於角色的策略 options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Administrator")); // 基於自訂要求的策略 options.AddPolicy("MinimumAge", policy => policy.Requirements.Add(new MinimumAgeRequirement(18))); }); builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); builder.Services.AddControllers(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();


步驟 2:定義自訂授權需求和處理程序

如果你使用自訂要求,你需要定義需求和處理程序。例如,定義一個最低年齡要求的需求和處理程序:

using Microsoft.AspNetCore.Authorization; using System; using System.Security.Claims; using System.Threading.Tasks; public class MinimumAgeRequirement : IAuthorizationRequirement { public int MinimumAge { get; } public MinimumAgeRequirement(int minimumAge) { MinimumAge = minimumAge; } } public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement) { if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth)) { return Task.CompletedTask; } var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value); int calculatedAge = DateTime.Today.Year - dateOfBirth.Year; if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge)) { calculatedAge--; } if (calculatedAge >= requirement.MinimumAge) { context.Succeed(requirement); } return Task.CompletedTask; } }


步驟 3:在控制器或動作方法中使用策略

你可以使用 [Authorize] 屬性來應用策略。例如:

using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; [Route("api/[controller]")] [ApiController] public class SampleController : ControllerBase { // 需要 Administrator 角色 [Authorize(Policy = "RequireAdministratorRole")] [HttpGet("admin")] public IActionResult GetForAdmin() { return Ok("This is an admin endpoint."); } // 需要滿足 MinimumAge 策略 [Authorize(Policy = "MinimumAge")] [HttpGet("age")] public IActionResult GetForAge() { return Ok("This is an age-restricted endpoint."); } }



完整範例

以下是完整的設定、需求和處理程序,以及控制器範例:

// Program.cs var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "yourissuer", ValidAudience = "youraudience", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("yoursecretkey")) }; }); builder.Services.AddAuthorization(options => { options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Administrator")); options.AddPolicy("MinimumAge", policy => policy.Requirements.Add(new MinimumAgeRequirement(18))); }); builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); builder.Services.AddControllers(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); // MinimumAgeRequirement.cs public class MinimumAgeRequirement : IAuthorizationRequirement { public int MinimumAge { get; } public MinimumAgeRequirement(int minimumAge) { MinimumAge = minimumAge; } } // MinimumAgeHandler.cs public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement) { if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth)) { return Task.CompletedTask; } var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value); int calculatedAge = DateTime.Today.Year - dateOfBirth.Year; if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge)) { calculatedAge--; } if (calculatedAge >= requirement.MinimumAge) { context.Succeed(requirement); } return Task.CompletedTask; } } // SampleController.cs [Route("api/[controller]")] [ApiController] public class SampleController : ControllerBase { [Authorize(Policy = "RequireAdministratorRole")] [HttpGet("admin")] public IActionResult GetForAdmin() { return Ok("This is an admin endpoint."); } [Authorize(Policy = "MinimumAge")] [HttpGet("age")] public IActionResult GetForAge() { return Ok("This is an age-restricted endpoint."); } }


通過這些步驟,你可以在 ASP.NET Core 8 中設定和使用自訂的授權策略,確保你的應用具備靈活而強大的授權控制。

JWT 的 issuer 是何作用?要如何設定其數值呢?

 在使用 JWT(JSON Web Token)時,issuer 是一個非常重要的屬性。它表示 JWT 的發行者,通常是用來識別誰生成和簽發了這個令牌。設定並驗證 issuer 可以增加 JWT 的安全性,防止令牌被偽造或來自不信任的發行者。

issuer 的作用

  1. 標識發行者issuer 表示令牌的生成者或發行者,通常是一個可信賴的服務或應用程序。
  2. 安全驗證:在驗證 JWT 時,可以檢查 issuer 是否匹配預期的值,以確保令牌是由受信任的發行者簽發的。
  3. 防止偽造:通過檢查 issuer,可以防止不受信任的來源生成和使用假令牌。

設定 issuer 的數值

issuer 通常是一個字符串,可以是 URL 或者其他能夠唯一標識發行者的字符串。這個值應該在發行令牌和驗證令牌的時候保持一致。

設定 issuer 的步驟

  1. 發行令牌時設置 issuer: 在生成 JWT 時,你需要在 SecurityTokenDescriptor 中設置 Issuer 屬性。

    csharp
    var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes("yoursecretkey"); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.Username) }), Expires = DateTime.UtcNow.AddHours(1), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), Issuer = "yourissuer", // 設置 issuer Audience = "youraudience" }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); return Ok(new { Token = tokenString });
  2. 驗證令牌時檢查 issuer: 在配置 JWT 驗證時,你需要設置 TokenValidationParameters 來檢查 Issuer

    csharp
    builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "yourissuer", // 設置 issuer 檢查 ValidAudience = "youraudience", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("yoursecretkey")) }; });

示例代碼

以下是完整的設定 issuer 的示例代碼:

發行令牌的控制器

csharp
[Route("api/[controller]")] [ApiController] public class AuthController : ControllerBase { [HttpPost("login")] public IActionResult Login([FromBody] LoginModel model) { if (model.Username == "test" && model.Password == "password") { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes("yoursecretkey"); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.Username) }), Expires = DateTime.UtcNow.AddHours(1), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), Issuer = "yourissuer", // 設置 issuer Audience = "youraudience" }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); return Ok(new { Token = tokenString }); } return Unauthorized(); } } public class LoginModel { public string Username { get; set; } public string Password { get; set; } }

配置身份驗證和授權

csharp
var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "yourissuer", // 設置 issuer 檢查 ValidAudience = "youraudience", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("yoursecretkey")) }; }); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();

注意事項

  • IssuerAudience 應該設置為能夠唯一標識你應用的值,並且應該保持一致。
  • yoursecretkey 應該是強隱私的隨機字串,並且應該安全存儲,不能公開暴露。
  • 這些設定應該在開發和生產環境中分別配置,避免敏感信息洩露。

這樣配置後,你的應用就能夠使用 JWT 進行身份驗證,並確保 JWT 是由受信任的發行者簽發的。