在控制器外部使用时,IHttpContextAccessor 包含空 User.Identity

我正在编写一个应用程序 ASP.Net Core (2.2) MVC。我需要根据登录用户的某些声明的值过滤 DbContext 内的一些数据。我注入 IHttpContextAccessor,但是当我尝试访问 HttpContext.User.Identity 时 - 所有属性均为 null,所有声明均为空。


这就是我试图实现连接 IHttpContextAccessor 的方式。我使用这样的标准方法:


public void ConfigureServices(IServiceCollection services){

  services.AddHttpContextAccessor();

...

}

然后我构建一个自定义提供程序来从用户中提取声明:


public class GetClaimsFromUser : IGetClaimsProvider

{

  public string UserId {get; private set;}


  public GetClaimsFromUser(IHttpContextAccessor accessor)

  {

     UserId = accessor.HttpContext?.User.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Name)?.Value;

  }

}

然后我还将它注入到ConfigureServices方法中:


public void ConfigureServices(IServiceCollection services){

...

  services.AddScoped<IGetClaimsProvider, GetClaimsFromUser>();

...

}

之后,我将其注入到 ApplicationDbContext 中,并尝试在构造函数中设置私有 _userId 字段:


public class ExpenseManagerDbContext: IdentityDbContext<ApplicationUser>

{

  private string _userId;


  public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IGetClaimsProvider claimsProvider) : base(options)

  {

    _userId = claimsProvider.UserId;

    ...

  }

...

}

而这里却是空的。当我访问控制器内的 HttpContext 时,User.Identity 不为空,一切都很好。但是,当我需要在控制器外部访问它时,它是空的。


谢谢你的帮助!!!


完整的代码可以在这里找到: https: //github.com/dudelis/expense-manager/blob/master/ExpenseManager.DataAccess/Concrete/EntityFramework/ExpenseManagerDbContext.cs?


拉莫斯之舞
浏览 192回答 2
2回答

郎朗坤

您正在尝试访问ExpenseManagerDbContext应用程序的IdentityDbContext.&nbsp;因此,它本身是身份验证系统的依赖项,并且将在框架执行身份验证时得到解决。所以流程有点像这样:请求进来了。身份验证中间件运行以对用户进行身份验证。UserManager解决了ExpenseManagerDbContext。ExpenseManagerDbContext解决了IGetClaimsProvider。GetClaimsProvider解析 HttpContext 并尝试访问用户的声明。身份验证中间件执行身份验证并设置HttpContext.User结果。如果您查看步骤 5 和 6,您将看到在身份验证中间件能够实际对用户进行身份验证并更新上下文上的用户对象之前访问 HttpContext 。由于身份验证中间件始终在请求开始时运行,因此情况始终如此。我建议您重新考虑,ExpenseManagerDbContext因为它可能不应该依赖于当前登录的用户。它应该独立于此。如果您的逻辑依赖于用户 ID,那么它可能应该是一个单独的服务。

慕莱坞森

解决了!问题在于 IdentityDbContext 和 ApplicationDataDbContext 共享相同的 DbContext。在我的控制器中,我有以下代码:[Authorize]public class AccountController : Controller{&nbsp; [HttpGet]&nbsp; public IActionResult Index()&nbsp; {&nbsp; &nbsp; &nbsp; var accounts = _accountService.GetAll();&nbsp; &nbsp; &nbsp; var models = _mapper.Map<List<AccountDto>>(accounts);&nbsp; &nbsp; &nbsp; return View(models);&nbsp; &nbsp;}}当我尝试从浏览器调用控制器时,由于 [Authorize] 属性,应用程序第一次初始化了 DbContext。这是在没有任何 HttpContext 的情况下完成的。因此,当应用程序在“_accountService.GetAll()”中调用 DbContext 时,DbContext 已经实例化,并且没有调用 Constructor 方法,因此,我的所有私有字段仍然为空!因此,我创建了第二个 DbContext 类,仅用于身份验证/授权目的。public class ApplicationDbAuthContext : IdentityDbContext{&nbsp; &nbsp; public ApplicationDbAuthContext(DbContextOptions<ApplicationDbAuthContext> options) : base(options)&nbsp; &nbsp; {&nbsp; &nbsp; }}因此,在控制器内发出请求期间,当我进行调用时会实例化正确的 DbContext,并且它包含 HttpContext。我将更新存储库中的代码以显示更改。同时,感谢您的所有回答。
打开App,查看更多内容
随时随地看视频慕课网APP