认证 vs 授权
认证(Authentication)
验证"你是谁"。即核实请求方的身份,通常通过密码、令牌、证书等凭证实现。ASP.NET Core 认证中间件(UseAuthentication)负责从请求中提取凭证并设置 HttpContext.User。
授权(Authorization)
验证"你能做什么"。在确认身份后,决定该用户是否有权访问特定资源或执行特定操作。ASP.NET Core 授权中间件(UseAuthorization)在认证之后执行,检查用户的 Claims、Roles 或自定义策略。
Claims(声明)
关于用户的键值对信息,存储在 ClaimsPrincipal 中。例如:sub(用户ID)、email、role(角色)、department(部门)等。JWT 的 Payload 本质上就是一组 Claims。
JWT Bearer 认证实战
JWT(JSON Web Token)是无状态认证的主流方案。服务端不存储 Session,每个请求携带包含用户信息的自签名令牌。
登录流程
──────────────────────────────────────────
客户端 API 服务器 数据库
│ │ │
│─── POST /auth/login ──────────▶│
│ { email, password } │
│ │──── 查询用户 ─▶│
│ │◀─── User ──── │
│ │ 验证密码 (BCrypt)
│ │ 生成 JWT (HS256/RS256)
│◀── { access_token, refresh_token } ──┤
│
后续请求
──────────────────────────────────────────
│─── GET /api/me ───────────────▶│
│ Authorization: Bearer <jwt> │
│ │ 验证签名 + 过期时间
│ │ 解析 Claims → HttpContext.User
│◀─── 200 { user data } ─────────│
// Program.cs — 配置 JWT Bearer 认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new()
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"]!)),
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
});
builder.Services.AddAuthorization();
// Token 生成服务
public class TokenService(IOptions<JwtOptions> opts)
{
public string GenerateAccessToken(AppUser user, IList<string> roles)
{
var claims = new List<Claim>
{
new(JwtRegisteredClaimNames.Sub, user.Id),
new(JwtRegisteredClaimNames.Email, user.Email!),
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(opts.Value.SecretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: opts.Value.Issuer,
audience: opts.Value.Audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(opts.Value.ExpiryMinutes),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
ASP.NET Core Identity
Identity 是完整的用户管理框架,提供注册、登录、密码 Hash(BCrypt/PBKDF2)、角色管理、声明管理、两步验证等功能。
// 注册 Identity(自定义用户类)
public class AppUser : IdentityUser
{
public string DisplayName { get; set; } = string.Empty;
public DateTimeOffset CreatedAt { get; set; }
}
builder.Services.AddIdentity<AppUser, IdentityRole>(options =>
{
options.Password.RequiredLength = 8;
options.Password.RequireDigit = true;
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
// 在 Controller 中使用 UserManager
public class AuthController(UserManager<AppUser> userManager, TokenService tokenService) : ControllerBase
{
[HttpPost("register")]
public async Task<IActionResult> Register(RegisterRequest req)
{
var user = new AppUser { UserName = req.Email, Email = req.Email };
var result = await userManager.CreateAsync(user, req.Password);
if (!result.Succeeded)
return BadRequest(result.Errors);
return Ok(new { Message = "注册成功,请查收验证邮件" });
}
}
基于策略的授权
// 注册授权策略
builder.Services.AddAuthorization(options =>
{
// 角色策略
options.AddPolicy("AdminOnly", p => p.RequireRole("Admin"));
// 声明策略
options.AddPolicy("PremiumUser", p =>
p.RequireClaim("subscription", "premium", "enterprise"));
// 复合策略
options.AddPolicy("SeniorEditor", p =>
p.RequireRole("Editor")
.RequireClaim("years_active")
.RequireAssertion(ctx =>
{
var years = ctx.User.FindFirstValue("years_active");
return int.TryParse(years, out var y) && y >= 3;
}));
});
// 在端点/Controller 上应用
[Authorize(Policy = "AdminOnly")]
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteUser(string id) { /* ... */ }
资源级别授权(IAuthorizationService)
// 自定义授权需求
public class PostOwnerRequirement : IAuthorizationRequirement { }
// 授权处理器
public class PostOwnerHandler : AuthorizationHandler<PostOwnerRequirement, Post>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext ctx,
PostOwnerRequirement requirement,
Post resource)
{
var userId = ctx.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (resource.Author.UserId == userId || ctx.User.IsInRole("Admin"))
ctx.Succeed(requirement);
return Task.CompletedTask;
}
}
// 在服务中使用
public class PostService(IAuthorizationService authz, IHttpContextAccessor http)
{
public async Task DeleteAsync(Post post)
{
var result = await authz.AuthorizeAsync(
http.HttpContext!.User,
post,
new PostOwnerRequirement());
if (!result.Succeeded)
throw new ForbiddenException("无权删除此文章");
// 执行删除...
}
}
Security Warning
JWT SecretKey 长度至少 256 位(32 字节),生产环境通过环境变量注入,绝不能硬编码在代码或配置文件中。刷新令牌必须存储在 HttpOnly Cookie 中(防 XSS),访问令牌存储在内存中(不存 localStorage)。令牌吊销需要黑名单机制(Redis 存储已吊销的 JTI)。
本章小结
JWT Bearer 提供无状态认证,适合前后端分离和微服务场景。ASP.NET Core Identity 处理用户注册/登录的复杂细节(密码哈希、账号锁定等)。基于策略的授权比简单的角色检查更灵活——Policy 可以组合任意 Claims 和自定义逻辑。IAuthorizationService 将权限检查从 Controller 下沉到业务层,实现资源级别的精细授权。