ASP.NET基于角色授权:登录后HttpContext.User为null问题求助
问题:HttpContext.User始终为null,JWT登录后无法获取当前用户信息
- 登录流程中Claims设置正确,
ClaimsPrincipal.Identity.Name可获取正确值,但登录后其他控制器中HttpContext.User始终为null - JWT令牌签发与验证均正常,登录功能可用,但
DeleteProject方法调用IsAdmin时,因HttpContext.User为null无法获取用户邮箱,导致admin判断始终返回false
相关代码
Startup.cs
public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddCors(); services.AddMvc().AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); }); services.AddMvc(options => { options.SuppressAsyncSuffixInActionNames = false; }); services.AddControllersWithViews() .AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore); services.AddAuthentication("JwtAuth") .AddCookie("JwtAuth", options => { options.LoginPath = "/login"; }); services.AddAuthorization(); services.AddScoped<IUserRepository, UserRepository>(); services.AddDbContext<ProPlanContext>(); services.AddHttpContextAccessor(); services.AddScoped<Jwt>(); services.AddSignalR().AddJsonProtocol(options => { options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); }); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Eckelmann Servie API", Version = "v1" }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Name = "Authorization", Description = "Specify the authorization token.", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }); c.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] { } }, }); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory ) { var logger = loggerFactory.CreateLogger<Startup>(); app.Use(async (context, next) => { try { logger.LogInformation("Data sent at " + DateTime.Now.ToString()); await Console.Out.WriteLineAsync("__________________________________________"); await next.Invoke(); } catch (Exception ex) { logger.LogInformation($"{ex.Message}"); } }); if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseCors(options => options .SetIsOriginAllowed(origin => true) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials() ); app.UseAuthentication(); app.UseRouting(); app.UseAuthorization(); app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Strict, HttpOnly = HttpOnlyPolicy.Always, Secure = CookieSecurePolicy.SameAsRequest }); app.Use(async (context, next) => { Thread.CurrentPrincipal = context.User; await next(context); }); app.UseEndpoints(endpoints => { endpoints.MapControllers(); try { endpoints.MapHub<Hub>("/signalr"); } catch (Exception ex) { Console.WriteLine(ex.Message); } }); } }
AuthenticationController.cs
[ApiController] [Route("api")] public class AuthenticationController: ControllerBase { private readonly Jwt _jwt; private readonly IUserRepository _userRepository; private readonly ProPlanContext _dbContext; public AuthenticationController(Jwt jwt, IUserRepository userRepository, ProPlanContext dbContext) { _jwt = jwt; _userRepository = userRepository; _dbContext = dbContext; } [HttpPost("login")] public IActionResult Login(LoginPoco loginPoco) { User? user = _userRepository.IdentifyUserViaEmail(loginPoco.Email); if (user != null && Verify(user, loginPoco.Password, 15000)) { var claimsPrincipal = GetClaimsPrincipal(user.Email); HttpContext.SignInAsync(claimsPrincipal).Wait(); var token = _jwt.IssueJwt(user.Id); var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = true, IsEssential = true }; Response.Cookies.Append("jwt", token, cookieOptions); return Ok(new { Token = token }); } else { return Unauthorized("Incorrect email or password!"); } } private bool Verify(User user, string enteredPassword, int iterations) { string hashedPassword = SignupController.HashPassword(user.Salt, enteredPassword, iterations); return hashedPassword == user.Password; } [HttpGet("login")] public IActionResult GetUserViaJwt() { try { var jwt = Request.Cookies["jwt"]; User? user = null; if (jwt != null) { JwtSecurityToken token = _jwt.ValidateJwt(jwt); int id = int.Parse(token.Issuer); user = _userRepository.IdentifyUserViaId(id); } return (user != null) ? Ok(user) as IActionResult : Unauthorized(); } catch (Exception) { return Unauthorized(); } } public static ClaimsPrincipal GetClaimsPrincipal(string email) { var claims = new List<Claim> { new Claim(ClaimTypes.Name, email), }; var claimsIdentity = new ClaimsIdentity(claims, "custom"); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); return claimsPrincipal; } [HttpPost("logout")] public IActionResult logout() { try { Response.Cookies.Delete("jwt"); return Ok(SuccessMessages.SuccessLogout); } catch(Exception ex) { return BadRequest(ex.Message); } } }
其他控制器中的相关方法
[HttpDelete("{id}")] public ActionResult DeleteProject(int id) { bool admin = IsAdmin() == true; if (!admin) { return Forbid(); } try { Project? project = _dbContext.Projects.Find(id); if (project == null) { return NotFound(); } _dbContext.Projects.Remove(project); _dbContext.SaveChanges(); return Ok(); } catch (Exception ex) { Log.Error(ex.ToString()); return Problem(ex.Message); } } public bool IsAdmin() { var email = HttpContext.User.FindFirst(ClaimTypes.Email)?.Value; User? user = _dbContext.Users.FirstOrDefault(user => user.Email == email); return user?.Role == "Administrator"; }
问题分析与修复方案
1. 认证方案配置错误
当前用AddCookie作为认证方案,但实际使用JWT令牌,需替换为JWT认证方案:
// 替换Startup.cs中ConfigureServices的认证配置 using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], // 替换为你的JWT发行方配置 ValidAudience = Configuration["Jwt:Audience"], // 替换为你的JWT受众配置 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"])) // 替换为你的密钥 }; // 从Cookie中读取JWT令牌 options.Events = new JwtBearerEvents { OnMessageReceived = context => { context.Token = context.Request.Cookies["jwt"]; return Task.CompletedTask; } }; });
2. Claims类型不匹配
IsAdmin方法查找ClaimTypes.Email,但GetClaimsPrincipal只添加了ClaimTypes.Name,需补充对应Claim:
public static ClaimsPrincipal GetClaimsPrincipal(string email) { var claims = new List<Claim> { new Claim(ClaimTypes.Name, email), new Claim(ClaimTypes.Email, email) // 添加该Claim }; var claimsIdentity = new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); return claimsPrincipal; }
3. 中间件顺序错误
UseCookiePolicy需放在UseAuthentication之前,同时移除不必要的Thread.CurrentPrincipal赋值:
// 调整Configure方法中的中间件顺序 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory ) { // ... 其他代码保持不变 ... app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Strict, HttpOnly = HttpOnlyPolicy.Always, Secure = CookieSecurePolicy.SameAsRequest }); app.UseAuthentication(); app.UseRouting(); app.UseAuthorization(); // 移除以下代码 // app.Use(async (context, next) => // { // Thread.CurrentPrincipal = context.User; // await next(context); // }); // ... 其他代码保持不变 ... }
4. 登录流程冗余代码
HttpContext.SignInAsync是Cookie认证的逻辑,改用JWT后可移除:
[HttpPost("login")] public IActionResult Login(LoginPoco loginPoco) { User? user = _userRepository.IdentifyUserViaEmail(loginPoco.Email); if (user != null && Verify(user, loginPoco.Password, 15000)) { var token = _jwt.IssueJwt(user.Id); var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = true, IsEssential = true }; Response.Cookies.Append("jwt", token, cookieOptions); return Ok(new { Token = token }); } else { return Unauthorized("Incorrect email or password!"); } }
内容的提问来源于stack exchange,提问作者Ghazal Nikmanesh
相关产品推荐
相关产品推荐

