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

ASP.NET JWT Bearer控制器无法获取用户数据问题排查

JWT Bearer 配置问题排查:Profile控制器无法获取用户数据

问题描述

我正在学习JWT Bearer的使用,不确定配置是否正确。注册流程一切正常,但在Profile控制器中无法获取用户数据,不清楚是项目配置还是控制器代码存在问题。

项目配置代码

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = "meuf", // 替换为你自己的值
        ValidAudience = "meufCorporation", // 替换为你自己的值
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")) // 替换为你自己的值
    };
}).AddCookie(options =>
{
    options.Cookie.Name = "Cookies";
});
builder.Services.AddAuthorization();

// 向容器添加服务
builder.Services.AddControllersWithViews();
var app = builder.Build();

// 配置HTTP请求管道
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // 默认HSTS值为30天,生产环境可根据需要修改
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.Use(async (context, next) =>
{
    // 从请求头获取令牌
    string token = context.Request.Headers["Authorization"];

    if (!string.IsNullOrEmpty(token) && token.StartsWith("Bearer "))
    {
        // 移除"Bearer "前缀
        token = token.Substring("Bearer ".Length).Trim();
    
        // 验证并解析令牌
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key)
        };
    
        try
        {
            var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
    
            if (principal.Identity is ClaimsIdentity identity)
            {
                // 获取令牌中的所有声明
                var claims = identity.Claims.ToList();
    
                // 创建name声明,值为用户标识,并添加到声明列表
                var userIdClaim = new Claim("name", principal.Identity.Name);
                claims.Add(userIdClaim);
    
                // 使用更新后的声明创建新的ClaimsIdentity
                var newIdentity = new ClaimsIdentity(claims, identity.AuthenticationType, identity.NameClaimType, identity.RoleClaimType);
    
                // 为当前用户设置新的ClaimsIdentity
                context.User = new ClaimsPrincipal(newIdentity);
            }
        }
        catch (Exception ex)
        {
            // 处理令牌解析或验证错误
            Console.WriteLine($"令牌验证错误: {ex.Message}");
        }
    }
    
    await next();
});

app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

控制器代码

[AutoValidateAntiforgeryToken]
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Profile()
{
    var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
    if (string.IsNullOrEmpty(userId))
    {
        return View(); // 处理用户未认证或无用户标识的情况
    }

    if (Guid.TryParse(userId, out Guid currentUserId))
    {
        var currentUser = await _dbContext.Users.FindAsync(currentUserId);
        if (currentUser == null)
        {
            return NotFound(); // 处理未找到用户的情况
        }

        ViewData["Username"] = currentUser.UserName;
        ViewData["Email"] = currentUser.Email;

        return View();
    }
    else
    {
        return View(); // 处理id无法转换为Guid的情况
    }
}

问题分析与修复方案

核心问题点

  1. 中间件顺序错误:app.UseAuthorization() 在 app.UseAuthentication() 之前执行,导致授权逻辑先于认证逻辑运行,此时User对象还未被认证中间件填充,自然无法获取用户数据。
  2. 冗余的自定义令牌解析中间件:你已经通过AddJwtBearer配置了官方的JWT认证中间件,无需自己编写令牌解析逻辑。自定义中间件不仅重复工作,还使用了不完整的验证参数(未验证Issuer、Audience、令牌有效期),且添加的name声明与控制器需要的ClaimTypes.NameIdentifier不匹配。
  3. 控制器特性配置不当:[AllowAnonymous] 会跳过授权检查,框架可能不会触发认证流程,导致User对象始终为空或未正确填充。

修复步骤

  1. 移除自定义令牌解析中间件:删除整个app.Use(async (context, next) => { ... })代码块,依赖官方AddJwtBearer的内置逻辑即可。
  2. 调整中间件顺序:将认证与授权中间件的顺序修正为:
    app.UseRouting();
    app.UseAuthentication(); // 先认证
    app.UseAuthorization();  // 后授权
    
  3. 修正控制器特性:移除[AllowAnonymous],添加[Authorize]确保只有认证用户能访问Profile接口:
    [AutoValidateAntiforgeryToken]
    [HttpGet]
    [Authorize] // 替换AllowAnonymous
    public async Task<IActionResult> Profile()
    {
        // 原有代码不变
    }
    
  4. 确保JWT令牌包含正确声明:在生成JWT令牌时,必须添加ClaimTypes.NameIdentifier类型的声明,值为用户的Guid ID,示例代码:
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // 关键声明
        new Claim(ClaimTypes.Name, user.UserName),
        // 其他需要的声明
    };
    
    var token = new JwtSecurityToken(
        issuer: "meuf",
        audience: "meufCorporation",
        claims: claims,
        expires: DateTime.Now.AddHours(1),
        signingCredentials: new SigningCredentials(
            new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")),
            SecurityAlgorithms.HmacSha256
        )
    );
    
    return new JwtSecurityTokenHandler().WriteToken(token);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:08:08