使用JSON Web Token(JWT)進行身分驗證與授權
JWT(JSON Web Token)是一種廣泛使用的身份驗證和授權方法。它通常用於傳輸用戶訊息,可以使用HMAC 演算法或 RSA 公鑰/私鑰對進行簽署。
我們預計將天氣預報資訊,設為需要進行驗證,才能讀取。
驗證流程
- 當使用者未進行驗證,進行訪問受保護資料時,顯示 401 錯誤
- 使用者登入,Server 認證成功後,傳送一組 JWT 給使用者。
- 使用者將取得 JWT,於 header 中的 Authorization 加上
Bearer token
。
- 重新訪問受保護資料,顯示結果。
- JWT 可以夾帶使用者資料,我們將訪問權限設為管理者,並嘗試不同角色的結果。
環境
- Microsoft.AspNetCore.Authentication.JwtBearer
安裝 Microsoft.AspNetCore.Authentication.JwtBearer
設置 Issuer 和 Key
在 appsettings.json
中,設置 Issuer 和 Key
1
2
3
4
|
"JwtSettings": {
"Issuer": "Solution1",
"SecretKey": "ZG0JZtDY3^FnkbCYy@!vJfVE922k9MJG"
}
|
設定驗證與授權
在 Program.cs
設定驗證,並讀取 JwtSettings 設定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
// 讀取 JwtSettings
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("JwtSettings"));
// 啟用 JwtBearer 驗證
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
// 驗證 Issuer
ValidateIssuer = true,
ValidIssuer = builder.Configuration.GetValue<string>("JwtSettings:Issuer"),
// 驗證 Audience
ValidateAudience = false,
// 驗證 Token 有效期間
ValidateLifetime = true,
// 驗證 SecurityKey
ValidateIssuerSigningKey = true,
// Key
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetValue<string>("JwtSettings:SecretKey")))
};
});
// 順序不能錯,先驗證再授權
app.UseAuthentication();
app.UseAuthorization();
|
設定授權才能讀取
1
2
3
4
5
6
7
8
|
[Authorize]
[HttpGet]
public async Task<IActionResult> GetWeatherForecasts([FromQuery] GetWeatherForecastsQuery request)
{
var query = await _mediator.Send(request);
return Ok(query);
}
|
顯示未授權結果
登入並取得Token
接下來我們設置登入處理,當帳號密碼正確時,則回傳 Token,大意如下
1
2
3
4
5
6
7
|
[HttpPost]
public async Task<IActionResult> Login([FromBody] LoginCommand command)
{
var result = await mediator.Send(command);
return result is null ? NotFound() : Ok(result);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class LoginCommandHandler : ICommandHandler<LoginCommand, string>
{
private readonly IUserRepository _userRepository;
private readonly IJwtService _jwtService;
public LoginCommandHandler(IUserRepository userRepository, IJwtService jwtService)
{
_userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository));
_jwtService = jwtService ?? throw new ArgumentNullException(nameof(jwtService));
}
public async Task<string> Handle(LoginCommand request, CancellationToken cancellationToken)
{
var user = _userRepository.Login(request.Email, request.Password);
if (user is null) return null;
var token = _jwtService.GenerateToken(user);
await Task.CompletedTask;
return token;
}
}
public class LoginCommand : ICommand<string>
{
public string Email { get; set; }
public string Password { get; set; }
}
|
產生 Token
其中 JwtService 就是我們要產生 Token 的服務,程式碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
// 如果 JwtService 放在 Infrastructure 層
// 可能要額外安裝 Microsoft.AspNetCore.Authentication.JwtBearer
public class JwtService : IJwtService
{
private readonly JwtSettings _options;
public JwtService(IOptions<JwtSettings> options)
{
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
}
public string GenerateToken(User user)
{
// 設定要加入到 Token 中的 Claims
var claims = new List<Claim>()
{
new Claim(JwtRegisteredClaimNames.Sub,user.Name),
new Claim(JwtRegisteredClaimNames.Email,user.Email),
new Claim(ClaimTypes.Role,user.Role)
};
// 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.SecretKey));
// HmacSha256 必須要大於 128 bits
var signinCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _options.Issuer,
audience: null,
claims: claims,
notBefore: null,
expires: DateTime.UtcNow.AddHours(1),
signinCredentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
|
測試登入後取得 Token
一開始有提到,JWT 是可以夾帶使用者資料的,我們可以透過 https://jwt.io/ 這個網站來看 Token 內容。
Swagger
通過授權查詢結果
Postman
參考
如何在 ASP.NET Core 3 使用 Token-based 身分驗證與授權 (JWT)
https://blog.miniasp.com/post/2019/12/16/How-to-use-JWT-token-based-auth-in-aspnet-core-31