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

ASP.NET Core中JWT令牌解码异常处理及无效令牌提示需求

解决ASP.NET Core中JWT令牌无效时返回友好提示的问题

问题分析

你的代码在JWT令牌被篡改(添加字符)时会触发未捕获的异常,原因是ReadJwtToken方法在令牌无效时会抛出SecurityTokenException,同时代码未处理空引用、令牌格式错误等边界情况,导致程序无法返回友好的无效提示。

修复后的代码

private readonly IHttpContextAccessor _contextAccessor;
private readonly ApplicationDbContext dbContext;

public UserPolicy(IHttpContextAccessor contextAccessor, ApplicationDbContext dbContext)
{
    _contextAccessor = contextAccessor;
    this.dbContext = dbContext;
}

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserPolicyRequirement requirement)
{
    var httpContext = _contextAccessor.HttpContext;
    if (httpContext == null)
    {
        context.Fail();
        return;
    }

    // 安全获取Action名称,避免空引用
    if (!httpContext.Request.RouteValues.TryGetValue("action", out var actionObj) || actionObj == null)
    {
        await SetInvalidTokenResponse(httpContext, "无法获取请求的Action信息");
        context.Fail();
        return;
    }
    string actionName = actionObj.ToString();

    // 安全获取Controller名称,避免空引用
    if (!httpContext.Request.RouteValues.TryGetValue("controller", out var controllerObj) || controllerObj == null)
    {
        await SetInvalidTokenResponse(httpContext, "无法获取请求的Controller信息");
        context.Fail();
        return;
    }
    string controllerName = controllerObj.ToString();

    var handler = new JwtSecurityTokenHandler();
    string token = httpContext.Request.Headers[HeaderNames.Authorization].ToString().Trim();

    if (string.IsNullOrEmpty(token))
    {
        await SetInvalidTokenResponse(httpContext, "未提供授权令牌");
        context.Fail();
        return;
    }

    // 验证令牌格式是否符合Bearer规范
    if (!token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
    {
        await SetInvalidTokenResponse(httpContext, "令牌格式错误,需以Bearer开头");
        context.Fail();
        return;
    }

    string jwtToken = token["Bearer ".Length..].Trim();

    try
    {
        // 读取并验证JWT令牌
        var jwtSecurityToken = handler.ReadJwtToken(jwtToken);
        
        // 检查令牌中是否存在userId声明
        var userIdClaim = jwtSecurityToken.Claims.FirstOrDefault(c => c.Type == "userId");
        if (userIdClaim == null)
        {
            await SetInvalidTokenResponse(httpContext, "令牌中缺少userId声明");
            context.Fail();
            return;
        }
        string userId = userIdClaim.Value;

        // 异步验证用户权限,避免线程阻塞
        bool hasAccess = await dbContext.userAccessActions.AnyAsync(a => 
            a.UserId == userId && 
            a.ActionName == actionName && 
            a.ControllName == controllerName);

        if (hasAccess)
        {
            context.Succeed(requirement);
        }
        else
        {
            await SetInvalidTokenResponse(httpContext, "您无权访问该接口");
            context.Fail();
        }
    }
    catch (SecurityTokenException ex)
    {
        // 捕获JWT无效相关异常
        await SetInvalidTokenResponse(httpContext, $"无效的授权令牌:{ex.Message}");
        context.Fail();
    }
    catch (Exception ex)
    {
        // 捕获其他未知异常
        await SetInvalidTokenResponse(httpContext, $"令牌处理失败:{ex.Message}");
        context.Fail();
    }
}

// 统一设置无效令牌的响应内容
private async Task SetInvalidTokenResponse(HttpContext httpContext, string message)
{
    httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
    httpContext.Response.ContentType = "application/json";
    var response = new { Status = "Error", Message = message };
    await httpContext.Response.WriteAsJsonAsync(response);
}

关键优化点

  • 异常捕获:专门捕获SecurityTokenException(JWT无效时抛出)和通用异常,避免程序崩溃,同时返回具体错误信息。
  • 边界处理:
    • 检查HttpContext是否为空
    • 安全获取路由的Action和Controller名称,避免空引用异常
    • 验证令牌是否为空、是否符合Bearer格式
  • 友好响应:通过统一方法返回JSON格式的错误提示,设置401未授权状态码
  • 异步优化:将同步的Any改为异步AnyAsync,提升性能
  • 声明校验:先判断userId声明是否存在,避免First方法抛出异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:35:28