ASP.NET Core 实战:基于 JWT Token 的权限控制全揭露

🔥关注墨瑾轩,带你探索编程的奥秘!🚀
🔥超萌技术攻略,轻松晋级编程高手🚀
🔥技术宝库已备好,就等你来挖掘🚀
🔥订阅墨瑾轩,智趣学习不孤单🚀
🔥即刻启航,编程之旅更有趣🚀

在这里插入图片描述在这里插入图片描述

在微服务和前后端分离的架构中,传统的 Session 认证早已力不从心。而 JWT(JSON Web Token)凭借其无状态、跨域友好、可扩展性强等特性,成为现代 Web 应用的“身份通行证”。本文将通过 JWT Token 的生成、验证、角色授权、自定义策略 四大核心模块,带你在 ASP.NET Core 中构建一套从“登录认证”到“细粒度权限控制”的完整解决方案。


第一章:JWT Token 的“三体法则”

1.1 JWT 的结构解析

JWT 由三部分组成:

  • Header:声明算法和 Token 类型(如 HS256)。
  • Payload:携带用户信息(如用户名、角色、过期时间)。
  • Signature:签名验证 Token 的完整性。
{
  "alg": "HS256",
  "typ": "JWT"
}
.
{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "Admin",
  "exp": 1516239022
}
.
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload), secretKey)

第二章:从零搭建 JWT 认证体系

2.1 安装依赖包

使用 NuGet 安装 JWT Bearer 认证中间件:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

2.2 配置 JWT 认证服务

Program.cs(或旧版 Startup.cs)中配置 JWT 认证服务:

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        // 配置 JWT 认证服务
        builder.Services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = builder.Configuration["Jwt:Issuer"],       // 从 appsettings.json 读取
                ValidAudience = builder.Configuration["Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"])),
                ClockSkew = TimeSpan.Zero // 严格校验过期时间
            };
        });

        // 添加授权服务
        builder.Services.AddAuthorization(options =>
        {
            options.AddPolicy("AdminOnly", policy =>
                policy.RequireRole("Admin")); // 基于角色的授权策略
        });

        var app = builder.Build();

        // 启用认证和授权中间件
        app.UseAuthentication();
        app.UseAuthorization();

        // 其他中间件...
        app.Run();
    }
}

关键注释

  • ValidIssuer / ValidAudience:签发者和受众,需与生成 Token 时一致。
  • IssuerSigningKey:对称密钥,务必保密!建议通过环境变量或 Azure Key Vault 管理。
  • ClockSkew:默认允许 5 分钟时钟偏差,此处设为 TimeSpan.Zero 以严格校验过期时间。

第三章:JWT Token 的“生成艺术”

3.1 登录接口生成 Token

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _config;

    public AuthController(IConfiguration config)
    {
        _config = config;
    }

    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginModel model)
    {
        // 模拟用户验证
        if (model.Username != "admin" || model.Password != "password")
            return Unauthorized();

        // 构建 Claims
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, model.Username),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(ClaimTypes.Role, "Admin") // 添加角色
        };

        // 配置 Token 参数
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:SecretKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var expires = DateTime.Now.AddDays(7); // Token 有效期

        // 生成 Token
        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: expires,
            signingCredentials: creds
        );

        return Ok(new
        {
            token = new JwtSecurityTokenHandler().WriteToken(token),
            expiration = token.ValidTo
        });
    }
}

关键注释

  • JwtRegisteredClaimNames:标准声明,如 sub(主体)、jti(唯一标识)。
  • ClaimTypes.Role:添加角色信息,用于后续授权。
  • expires:Token 过期时间,建议结合刷新 Token 机制延长会话。

第四章:权限控制的“双重奏”

4.1 基于角色的权限控制

使用 [Authorize(Roles = "Admin")] 限制特定角色访问:

[ApiController]
[Route("api/[controller]")]
[Authorize(Roles = "Admin")] // 仅 Admin 可访问
public class AdminController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return Ok("欢迎管理员!这是机密信息。");
    }
}

4.2 自定义授权策略

Program.cs 中定义基于策略的授权规则:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
        policy.RequireAssertion(context =>
        {
            var user = context.User;
            var birthYearClaim = user.FindFirst("BirthYear");
            if (birthYearClaim == null)
                return false;

            int birthYear = int.Parse(birthYearClaim.Value);
            int currentYear = DateTime.Now.Year;
            return currentYear - birthYear >= 18;
        }));
});

在控制器中使用策略:

[ApiController]
[Route("api/[controller]")]
[Authorize(Policy = "Over18")] // 仅 18 岁以上用户可访问
public class ContentController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return Ok("18+ 内容,请勿未成年人访问!");
    }
}

第五章:JWT 的“防坑指南”

5.1 常见问题及解决方案

5.1.1 问题:返回 401 未授权

原因

  • Token 过期
  • 签名错误(密钥不匹配)
  • 请求头未携带 Token

解决方案

  • 检查 SecretKey 是否与生成 Token 时一致
  • AddJwtBearer 中添加日志记录:
    .AddJwtBearer(options =>
    {
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                Console.WriteLine("JWT 认证失败: " + context.Exception.Message);
                return Task.CompletedTask;
            }
        };
    })
    
5.1.2 问题:角色信息无法获取

原因

  • 未在 Token 中添加 ClaimTypes.Role
  • 未启用 SaveToken = true

解决方案

  • 确保生成 Token 时包含角色声明
  • AddJwtBearer 中启用 SaveToken
    options.SaveToken = true;
    

第六章:JWT 的“进阶玩法”

6.1 刷新 Token 机制

实现 Token 刷新以延长会话:

[HttpPost("refresh")]
public async Task<IActionResult> Refresh([FromBody] RefreshTokenModel model)
{
    // 验证旧 Token
    var principal = GetPrincipalFromExpiredToken(model.Token);
    if (principal == null)
        return Unauthorized();

    // 验证 RefreshToken(需自行存储,如数据库或 Redis)
    var storedRefreshToken = _context.RefreshTokens
        .FirstOrDefault(t => t.Token == model.RefreshToken && t.UserId == principal.Identity.Name);
    if (storedRefreshToken == null || storedRefreshToken.Expires < DateTime.Now)
        return Unauthorized();

    // 生成新 Token
    var newToken = GenerateToken(principal.Claims.ToList());
    var newRefreshToken = GenerateRefreshToken(principal.Identity.Name);

    // 更新 RefreshToken
    storedRefreshToken.Token = newRefreshToken;
    storedRefreshToken.Expires = DateTime.Now.AddDays(7);

    return Ok(new
    {
        token = newToken,
        refreshToken = newRefreshToken
    });
}

关键注释

  • GenerateToken:与登录时逻辑相同。
  • GenerateRefreshToken:生成长生命周期的 RefreshToken,需安全存储。

JWT 的“终极奥义”

“JWT 不是万能的,但没有 JWT 是万万不能的!”

  • 无状态认证:JWT 的核心优势,适用于分布式系统。
  • 角色+策略:灵活组合满足复杂权限需求。
  • 刷新机制:平衡安全性与用户体验。

最后的吐槽

  • “Token 生成后怎么存?Local Storage vs Cookie?”
  • “密钥泄露了怎么办?快去换 Key Vault!”
  • “JWT 能加密吗?其实 JWE 才是正解!”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨瑾轩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值