身份验证和授权是每一个后端服务必不可少的,可以实现对非法请求进行拦截,能够有效保护数据的安全性。
JSON Web Token(JWT)是一项开放标准(RFC 7519),它定义了一种紧凑且自包含的方法,用于以 JSON 对象的形式在各方之间安全地传递信息。这些信息经过数字签名,因此可以被验证和信任。
JWT官网文档:JSON Web Token Introduction - jwt.io
一、配置身份验证和授权
1、添加身份验证和JWT授权库
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.IdentityModel.Tokens
2、添加JWT配置信息到appsettings.json文件中
"JwtTokenConfig": {
"Secret": "T#cx^Q$qsd8UrJMnY1(Vz$iie~lA3jgB96drYoPP4IDOffds&Qrw6GG+HClJteU#$)^JzMN_it#o*WE+*qVhE(_Ryy_t)",
"Issuer": "http://www.my.com/",
"Audience": "http://www.my.com/",
"AccessTokenExpiration": 240
}
3、创建JwtTokenConfig信息类
public class JwtTokenConfig
{
public string Secret { get; set; } = string.Empty;
public string Issuer { get; set; } = string.Empty;
public string Audience { get; set; } = string.Empty;
public int AccessTokenExpiration { get; set; }
}
4、启用身份验证和JWT授权服务
var builder = WebApplication.CreateBuilder(args);
JwtTokenConfig? jwtTokenConfig = builder.Configuration.GetSection("JwtTokenConfig").Get<JwtTokenConfig>();
if (jwtTokenConfig != null)
{
builder.Services.AddSingleton(jwtTokenConfig);
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = true;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtTokenConfig.Issuer,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtTokenConfig.Secret)),
ValidAudience = jwtTokenConfig.Audience,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
});
}
var app = builder.Build();
5、显示注册身份验证和授权
var app = builder.Build();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", () => "Hello World!");
app.Run();
注:身份验证中间件是在 CORS 中间件运行后运行的,所以需要显示注册身份验证和授权
6、在控制器基类中添加授权特性,对所有控制器施加授权验证
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize]
public abstract class BaseController : ControllerBase
{
}
二、生成JWT授权码
1、添加身份验证和JWT授权库
dotnet add package System.IdentityModel.Tokens.Jwt
dotnet add package Microsoft.IdentityModel.Tokens
2、创建JWT授权服务接口
public interface IJwtAuthService
{
string GenerateJwtToken(Claim[] claims);
}
3、创建JWT授权服务业务逻辑
public class JwtAuthService : IJwtAuthService
{
private readonly JwtTokenConfig _jwtTokenConfig;
public JwtAuthService(JwtTokenConfig jwtTokenConfig)
{
_jwtTokenConfig = jwtTokenConfig;
}
public string GenerateJwtToken(Claim[] claims)
{
bool shouldAddAudienceClaim = string.IsNullOrWhiteSpace(claims?.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Aud)?.Value);
JwtSecurityToken jwtToken = new(
_jwtTokenConfig.Issuer,
shouldAddAudienceClaim ? _jwtTokenConfig.Audience : string.Empty,
claims,
expires: DateTime.Now.AddMinutes(_jwtTokenConfig.AccessTokenExpiration),
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(_secret), SecurityAlgorithms.HmacSha256Signature));
return new JwtSecurityTokenHandler().WriteToken(jwtToken);
}
}
4、注册JWT授权服务
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IJwtAuthService, JwtAuthService>();
var app = builder.Build();
5、在授权控制器中使用JWT服务,生成Token
public class AuthController : BaseController
{
private readonly IJwtAuthService _authService;
public AuthController(IAuthService authService)
{
_authService = authService;
}
[AllowAnonymous]
[HttpPost]
public IActionResult Login([FromBody] LoginRequest request)
{
// 1. 验证用户名密码(伪代码)
if (!IsValidUser(request.User, request.Password))
return Unauthorized();
// 2. 创建JWT声明(伪代码)
string roleName = "User";
Claim[] claims =
[
new Claim(ClaimTypes.NameIdentifier, user),
new Claim(ClaimTypes.Role, roleName)
];
// 3. 生成 JWT Token
var token = _authService.GenerateJwtToken(claims);
// 4. 返回 Token
return Ok(new { Token = token });
}
private bool IsValidUser(string user, string password)
{
// 实际应该查数据库(伪代码)
return user == "admin" && password == "123456";
}
}
注:使用ClaimTypes.NameIdentifier来声明用户标识,可以在集成SignalR时使SignalR很容易获取到用户标识并进行消息发送,因为SignalR默认获取的用户标识就是ClaimTypes.NameIdentifier