如何使用ASP.NET Core从DbContext中获取JWT的用户名?

时间:2018-06-13 12:21:17

标签: c# asp.net-core .net-core jwt claims-based-identity

MyDbContext 中,我有方法LogChanges,它使用以下信息记录 logs 表中的任何更改:

TableName = entityName,
IDRow = JsonConvert.SerializeObject(primaryKeys),
Value = JsonConvert.SerializeObject(values),
Date = dateTimeNow,
Author = userFromJWT

我想将作者设置为JWT授权的用户。从这部分开始:

  

" sub":" myUserName"

如何在 MyDbContext 中获取该用户名?也许某种依赖注射?

提前致谢!

@Solution

Startup.cs

   public void ConfigureServices(IServiceCollection services) {
       // ...
       services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
           .AddJwtBearer(options => {
          options.TokenValidationParameters = new TokenValidationParameters {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Issuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
          };
        });
      services.AddHttpContextAccessor();
      //...
    }

MyDbContext.cs

// ...
private readonly IHttpContextAccessor _httpContext;

public MyDbContext(DbContextOptions options, IHttpContextAccessor httpContext) : base(options) {
  _httpContext = httpContext;
}
//..

并从我使用的JWT索取声明(来自" sub")

_httpContext.HttpContext.User.Claims.SingleOrDefault(
        c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value

3 个答案:

答案 0 :(得分:5)

假设您实际上已集成到ASP.NET Core身份验证子系统(即services.AddAuthenticationapp.UseAuthentication)中,那么这基本上是为您处理的。将读取JWT以构建一个ClaimsPrincipal实例,然后将其存储在HttpContext.User中。因此,用户的用户名将位于HttpContext.User.Identity.Name的标准位置,或者您可以通过Claims上的HttpContext.User.Identity集合直接访问(以及任何其他声明)。

如果问题是您需要将此信息放在您无权直接访问HttpContext.User的地方(基本上是控制器或视图之外的任何地方),那么您只需要注入IHttpContextAccessor 。这需要两件事:

  1. 您必须添加IHttpContextAccessor服务。出于性能原因,默认情况下不包括此内容。 (这并不是说它会对性能产生严重影响。只是如果你不需要它,你可以通过不包括它来获得更多的性能.ASP.NET Core只包含你的内容需要包括。)无论如何:

    ASP.NET Core 2.1

    services.AddHttpContextAccessor();
    

    以前的版本

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
  2. 无论您何时注入,都需要成为请求管道的一部分,否则HttpContext将不存在。这应该不是问题,因为你依赖于存在JWT,无论如何。请记住,您不能在常规控制台应用程序等中使用它。

答案 1 :(得分:3)

是的,我在@Chris Prat的解决方案中看到的唯一问题是,现在您需要在实际上与它无关的项目中引用Asp.Net.Core程序集。 对我来说,更好的解决方案是定义一个具有所需属性的新类。然后使用DI / IOC将其注册为Func,并将其传递给DBContext。 即。

public class UserInfo
{
    public Guid UserId{get;set;}
    public string UserName{get;set;
}

然后在Startup.cs中执行以下操作:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    ... services registration part ommited

    var builder = new ContainerBuilder();
    builder.Populate(services);
    builder.Register(context=>
    {
        var identityUser = context.Resolve<IHttpContextAccessor>()?.HttpContext?.User;
        var userInfo = new UserInfo()
        {
            Name=//get it from identityUser.Claims 
            Id= //get it from identityUser.Claims
        }
        return userInfo;
    }).AsSelf()
      .InstancePerLifetimeScope();
}

然后在DbContext中有了这个(这里我使用的是Autofac IOC容器,但是任何可以注册工厂的容器都可以做到这一点,例如StructureMap,Ninject,Autofac ...):

public class MyDbContext: DbContext
{
    private readonly Func<UserInfo> _userInfoFactory;
    private UserInfo UserInfo => _userInfoFactory();

    public MyDbContext(DbContextOptions options, Func<UserInfo> userInfoFactory) : base(options) 
    {
        this._userInfoFactory = userInfoFactory;
    }

    public void SomeMethod()
    {
        var someEntity = new SomeEntity()
        {
           ChangedByUserId = this.UserInfo.Id
           ...
        }
     }  
}

这是一种更清洁的解决方案,可导致项目之间更多的去耦。

答案 2 :(得分:1)

添加到您的 Startup.cs ConfigureServices 方法

services.AddHttpContextAccessor();

在您的存储库中,在构造函数中使用依赖注入来添加 IHttpContentAccessor,您可以从声明中获取 UserId

public ModelRepository(DataContext dataContext, ILogger<ModelRepository> logger, IHttpContextAccessor httpContextAccessor)
        {
            _dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
            _logger = logger;

            if(httpContextAccessor.HttpContext.User.Identity.IsAuthenticated)
            {
                userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
            }           
        }

如果你需要用户的更多信息,你也可以注入 UserManager

public ModelRepository(DataContext dataContext, ILogger<ModelRepository> logger, IHttpContextAccessor httpContextAccessor, UserManager<ApplicationUser> userManager)
        {
            _dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
            _logger = logger;

            if(httpContextAccessor.HttpContext.User.Identity.IsAuthenticated)
            {
                userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
                user = await userManger.FindByIdAsync(userId);
            }           
        }