Как получить имя пользователя из JWT в DbContext с помощью ASP.NET Core? - PullRequest
0 голосов
/ 13 июня 2018

В 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;
}
//..

и для получения имени из претензий (от "sub") от JWT я использовал

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

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Да, единственная проблема, которую я вижу с решением @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 у вас есть это (здесь я использую контейнер IOC Autofac, но любой контейнер, которыйможет зарегистрировать фабрики, может сделать это как 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
           ...
        }
     }  
}

Это более чистое решение, которое приводит к большей развязке между проектами.

0 голосов
/ 13 июня 2018

Предполагая, что вы на самом деле интегрированы в подсистему аутентификации ASP.NET Core (т. Е. services.AddAuthentication и app.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.Просто помните, что вы не можете использовать это в обычном консольном приложении и т. Д.

...