日志25-7-17
本文最后更新于 2025年7月24日星期四 17:34
今天学习完EF Core,开始做DEMO【图书管理系统】
注解校验
ModelState.IsValid
:判断前端传来的字段是否符合实体类上的注解约束声明
自动更新UpdatedTime字段
自定义审计接口
1 |
|
实体类实现接口
1 |
|
在DbContext中重写SaveChanges()和SaveChangesAsync()
1 |
|
1 |
|
生成基础CRUD
联结实体类
书 和 书的类别 是多对多的关系:
BookCategory:
1 |
|
ApplicationDbContext:
1 |
|
JWT
使用步骤
- 安装2个NuGet包
1
2dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt - 在
appsettings.json
中配置 JWT1
2
3
4
5
6"JwtSettings": {
"_comment": "SecretKey:令牌的颁发者(通常是你的API域名);Issuer:令牌的接收者(通常是你的前端域名或客户端应用标识)",
"SecretKey": "这是一个非常非常非常非常非常安全的秘密密钥!长度必须足够长且复杂,例如至少32个随机字符。",
"Issuer": "YourApiIssuer",
"Audience": "YourApiClient"
} - 在
Program.cs
中添加认证支持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// JWT配置开始
IConfigurationSection jwtSettings = builder.Configuration.GetSection("JwtSettings");
string secretKey = jwtSettings["SecretKey"]; // JWT签名密钥
string issuer = jwtSettings["Issuer"]; // JWT令牌发布者
string audience = jwtSettings["Audience"]; // JWT令牌接收者
if (string.IsNullOrEmpty(secretKey))
{
throw new InvalidOperationException("JWT 密钥没有配置好");
}
byte[] key = Encoding.ASCII.GetBytes(secretKey); // 将密钥字符串转为字节数组
builder.Services.AddAuthentication(options =>
{
// 设置默认的认证方案和挑战方案为JWT Bearer
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
// 在开发环境可以设置为false,生产环境必须设置为true,强制HTTPS
options.RequireHttpsMetadata = false;
options.SaveToken = true; // 是否在HttpContext中存储JWT
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true, // 验证签名密钥,确保令牌没有被篡改
IssuerSigningKey = new SymmetricSecurityKey(key), // 用于签名的密钥
ValidateIssuer = true, // 验证令牌的发布者
ValidateAudience = true, // 验证令牌的接收者
ValidateLifetime = true, // 验证令牌有效期(过期时间)
ValidIssuer = issuer, // 有效的发布者
ValidAudience = audience, // 有效的接收者
ClockSkew = TimeSpan.Zero // 令牌有效期时钟偏移量,默认为5分钟,这里设置为0,表示令牌有效期必须与当前时间一致
};
});
builder.Services.AddAuthentication(); // 启用授权服务
// JWT配置结束 - 创建JWT 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
36private (string Token, DateTime ExpiresAt) GenerateJwtToken(User user)
{
var jwtSettings = _configuration.GetSection("JwtSettings");
var secretKey = jwtSettings["SecretKey"]!;
var issuer = jwtSettings["Issuer"]!;
var audience = jwtSettings["Audience"]!;
var key = Encoding.ASCII.GetBytes(secretKey);
// JWT 声明 (Claims):这些信息将包含在令牌中
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // 用户唯一标识符
new Claim(ClaimTypes.Name, user.Name), // 用户名
new Claim(ClaimTypes.Email, user.Email), // 邮箱
// 可以根据需要添加其他自定义声明,如用户角色
new Claim(ClaimTypes.Role, nameof(user.Role)), // 假设 User 实体有 Role 属性
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // JWT 的唯一 ID,用于黑名单(可选)
};
var tokenHandler = new JwtSecurityTokenHandler();
var expires = DateTime.UtcNow.AddHours(1); // 令牌有效期为 1 小时,可以根据需求调整
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = expires,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Issuer = issuer,
Audience = audience
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return (tokenHandler.WriteToken(token), expires);
} - 使用注解保护API
[Authorize]
:必须通过JWT的考验[AllowAnonymous]
:允许匿名访问 - 客户端如何存储和发送JWT
TOKEN存储在本地,每次发请求的时候把TOKEN放在HTTP头的authorization里:Bearer 这里写TOKEN串
日志25-7-17
https://zhiyun.blog/日志25-7-17/