Google Code Prettify

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

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 的優點在於
其無狀態性,適合分布式系統中的身份驗證,但同時也需要考慮安全性問題,例如令牌過期時間、刷新
令牌的安全管理等。

存取的 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。

如何使用HttpClient存取具有Authorize的RESTfull API?

要使用 `HttpClient` 存取需要授權的 RESTful API,你通常需要在請求中附加適當的身份驗證憑證,例如 JWT(JSON Web Token)。以下是完整的步驟,包括如何生成 JWT、配置 `HttpClient` 並發送授權請求。

### 假設場景
- 你的 RESTful API 使用 JWT 作為身份驗證方式。
- 你已經擁有用戶憑證(如用戶名和密碼)並且能夠獲取 JWT。

### 步驟 1:生成 JWT
首先,你需要獲取 JWT,通常是通過登錄端點獲取。這個端點驗證用戶名和密碼並返回一個 JWT。


public async Task GetJwtTokenAsync(string username, string password)
{
    var client = new HttpClient();
    var loginUrl = "https://yourapi.com/api/auth/login";

    var loginData = new
    {
        Username = username,
        Password = password
    };

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

    response.EnsureSuccessStatusCode();

    var responseString = await response.Content.ReadAsStringAsync();
    var jwt = JsonSerializer.Deserialize(responseString);

    return jwt.Token;
}

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


### 步驟 2:配置 HttpClient 並附加 JWT
在獲取 JWT 之後,你可以在後續的請求中附加此 JWT 進行授權。


public async Task GetProtectedResourceAsync(string token)
{
    var client = new HttpClient();
    var requestUrl = "https://yourapi.com/api/protectedresource";

    // 附加 Bearer Token 到請求頭
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var response = await client.GetAsync(requestUrl);

    response.EnsureSuccessStatusCode();

    return await response.Content.ReadAsStringAsync();
}


### 完整示例
以下是完整的代碼,包含了從獲取 JWT 到使用 JWT 訪問受保護資源的流程:


using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class Program
{
    public static async Task Main(string[] args)
    {
        string username = "yourusername";
        string password = "yourpassword";

        string token = await GetJwtTokenAsync(username, password);
        string protectedResource = await GetProtectedResourceAsync(token);

        Console.WriteLine(protectedResource);
    }

    public static async Task GetJwtTokenAsync(string username, string password)
    {
        var client = new HttpClient();
        var loginUrl = "https://yourapi.com/api/auth/login";

        var loginData = new
        {
            Username = username,
            Password = password
        };

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

        response.EnsureSuccessStatusCode();

        var responseString = await response.Content.ReadAsStringAsync();
        var jwt = JsonSerializer.Deserialize(responseString);

        return jwt.Token;
    }

    public static async Task GetProtectedResourceAsync(string token)
    {
        var client = new HttpClient();
        var requestUrl = "https://yourapi.com/api/protectedresource";

        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var response = await client.GetAsync(requestUrl);

        response.EnsureSuccessStatusCode();

        return await response.Content.ReadAsStringAsync();
    }
}

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


### 注意事項
1. **安全性**:確保密碼和其他敏感信息的安全傳輸和存儲。
2. **錯誤處理**:添加適當的錯誤處理邏輯,例如處理登錄失敗或令牌過期的情況。
3. **Token 更新**:考慮在令牌過期後刷新令牌。

這樣,你就可以使用 `HttpClient` 訪問需要授權的 RESTful API。這種方式適用於各種需要身份驗證的 HTTP 請求場景。

ASP.NET Core 中,生成基於角色(Role)的授權

在 ASP.NET Core 中,生成基於角色的授權策略是非常常見的做法。這些策略可以用來限制只有特定角色的用戶才能訪問某些資源。以下是如何生成和使用基於角色的授權策略的完整示例。 步驟 1:設定角色和用戶 首先,你需要確保你的身份驗證系統已經正確設定,並且用戶擁有相關的角色。假設你使用的是 ASP.NET Core Identity,這是如何為用戶分配角色的示例。

    
public class SeedData
{
    public static async Task Initialize(IServiceProvider serviceProvider, UserManager<ApplicationUser> userManager)
    {
        var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();

        string[] roleNames = { "Admin", "User" };
        IdentityResult roleResult;

        foreach (var roleName in roleNames)
        {
            var roleExist = await roleManager.RoleExistsAsync(roleName);
            if (!roleExist)
            {
                roleResult = await roleManager.CreateAsync(new IdentityRole(roleName));
            }
        }

        var user = await userManager.FindByEmailAsync("admin@admin.com");
        if (user == null)
        {
            user = new ApplicationUser
            {
                UserName = "admin@admin.com",
                Email = "admin@admin.com",
            };
            await userManager.CreateAsync(user, "Password123!");
            await userManager.AddToRoleAsync(user, "Admin");
        }
    }
}
   
  

步驟 2:配置角色策略 在 Program.cs 中,你可以配置角色策略。這裡我們會設定一個基於角色的策略,確保只有 Admin 角色的用戶可以訪問某些資源。


var builder = WebApplication.CreateBuilder(args);

// 設定身份驗證和 JWT Bearer
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("AdminOnly", policy => policy.RequireRole("Admin"));
});

builder.Services.AddControllers();

var app = builder.Build();

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

app.MapControllers();

app.Run();

步驟 3:在控制器中使用策略 在控制器中應用你定義的策略。你可以使用 [Authorize] 屬性來限制訪問。


using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
[ApiController]
public class SampleController : ControllerBase
{
    // 只有 Admin 角色的用戶可以訪問這個端點
    [Authorize(Policy = "AdminOnly")]
    [HttpGet("admin")]
    public IActionResult GetForAdmin()
    {
        return Ok("This is an admin endpoint.");
    }

    // 所有經過身份驗證的用戶都可以訪問這個端點
    [Authorize]
    [HttpGet("user")]
    public IActionResult GetForUser()
    {
        return Ok("This is a user endpoint.");
    }
}
    

完整範例 以下是完整的設定,包括初始化角色和用戶、配置授權策略,以及在控制器中使用這些策略的示例:


// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity()
    .AddEntityFrameworkStores()
    .AddDefaultTokenProviders();

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("AdminOnly", policy => policy.RequireRole("Admin"));
});

builder.Services.AddControllers();

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var userManager = services.GetRequiredService>();
    SeedData.Initialize(services, userManager).Wait();
}

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

app.MapControllers();

app.Run();

// SeedData.cs
public class SeedData
{
    public static async Task Initialize(IServiceProvider serviceProvider, UserManager userManager)
    {
        var roleManager = serviceProvider.GetRequiredService>();

        string[] roleNames = { "Admin", "User" };
        IdentityResult roleResult;

        foreach (var roleName in roleNames)
        {
            var roleExist = await roleManager.RoleExistsAsync(roleName);
            if (!roleExist)
            {
                roleResult = await roleManager.CreateAsync(new IdentityRole(roleName));
            }
        }

        var user = await userManager.FindByEmailAsync("admin@admin.com");
        if (user == null)
        {
            user = new ApplicationUser
            {
                UserName = "admin@admin.com",
                Email = "admin@admin.com",
            };
            await userManager.CreateAsync(user, "Password123!");
            await userManager.AddToRoleAsync(user, "Admin");
        }
    }
}

// SampleController.cs
[Route("api/[controller]")]
[ApiController]
public class SampleController : ControllerBase
{
    [Authorize(Policy = "AdminOnly")]
    [HttpGet("admin")]
    public IActionResult GetForAdmin()
    {
        return Ok("This is an admin endpoint.");
    }

    [Authorize]
    [HttpGet("user")]
    public IActionResult GetForUser()
    {
        return Ok("This is a user endpoint.");
    }
}

// ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
}

// ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions options)
        : base(options)
    {
    }
}

這樣的配置可以確保你能夠靈活地使用基於角色的策略,限制訪問特定資源,使應用更具安全性和可控性。

使用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 是由受信任的發行者簽發的。