You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core Web API中JWT令牌用户有效性验证问题

这确实是JWT认证的一个常见痛点——令牌一旦签发,默认情况下只要没过期就会被认可,完全不会去校验用户当前的状态。我之前也帮不少开发者解决过这个问题,用自定义授权策略就能完美搞定,给你一步步拆解:

解决方案:自定义授权策略校验用户实时状态

1. 创建自定义授权需求和处理器

首先我们需要定义一个授权需求标识,然后写对应的处理器,在处理器里去你的用户存储(数据库/缓存等)校验用户是否存在且未被停用。

先创建一个空的需求类(仅作为策略标识,不需要额外参数):

public class ActiveUserRequirement : IAuthorizationRequirement
{
    // 无额外属性,仅用于标记该授权需求
}

然后编写授权处理器,这里需要注入你的用户服务(比如IUserService)来查询用户实时状态,同时注入IHttpContextAccessor获取当前用户的标识:

public class ActiveUserHandler : AuthorizationHandler<ActiveUserRequirement>
{
    private readonly IUserService _userService;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ActiveUserHandler(IUserService userService, IHttpContextAccessor httpContextAccessor)
    {
        _userService = userService;
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ActiveUserRequirement requirement)
    {
        // 从JWT Claims中获取用户唯一标识(这里用NameIdentifier,你也可以用自定义的Claim类型)
        var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
        
        if (string.IsNullOrEmpty(userId))
        {
            context.Fail();
            return;
        }

        // 调用用户服务查询用户实时状态
        var user = await _userService.GetUserByIdAsync(Guid.Parse(userId));
        if (user != null && !user.IsDisabled)
        {
            // 用户存在且未停用,授权通过
            context.Succeed(requirement);
        }
        else
        {
            // 用户不存在或已停用,授权失败
            context.Fail();
        }
    }
}

2. 在Startup.cs中注册策略和处理器

接下来在ConfigureServices方法里,把处理器注册到DI容器,然后添加自定义授权策略:

public void ConfigureServices(IServiceCollection services)
{
    // 保留你原本的JWT认证配置
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                // 你的其他JWT配置项...
            };
        });

    // 注册授权处理器
    services.AddScoped<IAuthorizationHandler, ActiveUserHandler>();
    // 注册HttpContextAccessor(如果之前没注册过)
    services.AddHttpContextAccessor();

    // 添加自定义授权策略
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ActiveUser", policy =>
        {
            // 先要求用户已通过JWT认证
            policy.RequireAuthenticatedUser();
            // 加入我们的自定义用户状态校验需求
            policy.AddRequirements(new ActiveUserRequirement());
        });
    });

    // 注册你的用户服务(示例)
    services.AddScoped<IUserService, UserService>();
    services.AddControllers();
}

3. 在接口上使用自定义策略

现在你只需要在需要校验用户实时状态的接口上,用[Authorize(Policy = "ActiveUser")]代替原来的[Authorize]即可:

[ApiController]
[Route("api/[controller]")]
public class ProtectedController : ControllerBase
{
    // 这个接口会同时校验JWT有效性 + 用户实时状态
    [Authorize(Policy = "ActiveUser")]
    [HttpGet("sensitive-operation")]
    public IActionResult PerformSensitiveOperation()
    {
        return Ok("仅活跃用户可执行此操作");
    }

    // 普通接口仍使用原[Authorize],仅校验JWT有效性
    [Authorize]
    [HttpGet("basic-info")]
    public IActionResult GetBasicInfo()
    {
        return Ok("JWT有效即可访问");
    }
}

额外优化建议

  • 缓存优化:如果高并发场景下每次请求查数据库压力大,可以给用户状态加个短时间缓存(比如5分钟),平衡实时性和性能。
  • 错误提示:可以在处理器或自定义中间件中,给授权失败的请求返回更友好的错误信息(比如403 Forbidden + "用户已被停用或不存在")。
  • Claims补充:如果你的业务允许,可以在令牌刷新时同步更新用户状态相关的Claims,但这只能作为辅助,实时查询数据库才是最可靠的方式。

这样配置后,即使用户的JWT尚未过期,只要他被删除或停用,访问带有ActiveUser策略的接口时就会被拒绝,完美解决你的问题!

内容的提问来源于stack exchange,提问作者David Flury

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:31:46