.NET Core应用中HttpContext.User未填充,User.GetUserId()返回0问题
问题描述
调用User.GetUserId()始终返回0,且User.IsAuthenticated = false。Postman和Angular客户端均能成功获取Token,请求时也已将Token添加到请求头的Authorization字段(格式为Bearer <token>),但调用包含User.GetUserId()的其他接口时仍得到相同结果。
更新TokenService后,登录控制器中调用CreateToken方法后能从User获取正确ID,但其他接口的HttpContext.User仍为空(已注入IHttpContextAccessor)。
相关代码
Startup.cs 的 Configure 方法
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseCors(x => x .AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin() .AllowCredentials() .WithOrigins("https://localhost:4200")); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapFallbackToController("Index", "Fallback"); }); }
AccountController 的 Login 方法
[HttpPost("login")] public async Task<ActionResult<UserDto>> Login(LoginDto loginDto) { var user = await _userManager.Users .Include(p => p.Photos) .SingleOrDefaultAsync(x => x.UserName == loginDto.Username.ToLower()); if (user == null) return Unauthorized("Invalid username"); var result = await _signInManager .CheckPasswordSignInAsync(user, loginDto.Password, false); if (!result.Succeeded) return Unauthorized(); var token = await _tokenService.CreateToken(user); var userDto = new UserDto { Username = user.UserName, Token = token, KnownAs = user.KnownAs, Gender = user.Gender }; var uId = User.GetUserId(); // 此处返回0 return userDto; }
TokenService.cs
public class TokenService : ITokenService { private readonly SymmetricSecurityKey _key; private readonly UserManager<AppUser> _userManager; private readonly IHttpContextAccessor _httpContextAccessor; // 更新后新增注入 public TokenService(IConfiguration config, UserManager<AppUser> userManager, IHttpContextAccessor httpContextAccessor) { _userManager = userManager; _key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"])); _httpContextAccessor = httpContextAccessor; } public async Task<string> CreateToken(AppUser user) { var claims = new List<Claim> { new Claim(JwtRegisteredClaimNames.NameId, user.Id.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName.ToString()) }; var roles = await _userManager.GetRolesAsync(user); claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Expires = DateTime.Now.AddDays(7), SigningCredentials = creds }; // UPDATE: 手动设置HttpContext.User的代码 var identity = new ClaimsIdentity(claims); var claimsPrincipal = new ClaimsPrincipal(identity); _httpContextAccessor.HttpContext.User = claimsPrincipal; var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } }
问题分析与解决方案
1. Login接口中User.GetUserId()返回0的原因
登录请求本身未经过认证流程,此时User对象还未被认证中间件填充,所以无法获取到用户ID。直接使用查询到的user.Id即可,无需通过User对象获取。
修改Login方法中的代码:
// 替换原代码 var uId = user.Id;
2. 其他接口User.IsAuthenticated = false的核心问题
你的项目缺少JWT认证方案的配置,仅调用app.UseAuthentication()不足以让系统自动解析请求头中的Bearer Token,导致认证失败,User始终处于未认证状态。
解决步骤:
在Startup.cs的ConfigureServices方法中添加JWT认证配置:
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; // ... public void ConfigureServices(IServiceCollection services) { // 其他服务配置... // 添加JWT认证 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, // 确保此处的TokenKey与TokenService中使用的一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["TokenKey"])), ValidateIssuer = false, // 根据你的需求调整,若有签发方则设置为true并配置ValidIssuer ValidateAudience = false // 根据你的需求调整,若有受众则设置为true并配置ValidAudience }; }); // 其他服务配置... }
3. TokenService中手动设置HttpContext.User的错误
手动设置HttpContext.User仅对当前登录请求生效,后续请求的上下文是独立的,这一操作完全多余且会导致逻辑混乱。删除这部分代码:
// 删掉以下代码 var identity = new ClaimsIdentity(claims); var claimsPrincipal = new ClaimsPrincipal(identity); _httpContextAccessor.HttpContext.User = claimsPrincipal;
4. 验证中间件顺序
你的Configure方法中中间件顺序是正确的,保持以下顺序即可:UseRouting → UseCors → UseAuthentication → UseAuthorization → UseEndpoints
5. 测试注意事项
确保请求头的Authorization格式完全正确:Bearer <token>,Token前后无多余空格,且Token是完整有效的。
内容的提问来源于stack exchange,提问作者user3748973

