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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:24:57