Chapter 06

认证与授权

从 ASP.NET Core Identity 到 JWT Bearer、OAuth2/OIDC,掌握现代 .NET 应用的身份认证完整体系。

认证 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 下沉到业务层,实现资源级别的精细授权。