在 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
答案 0 :(得分:5)
假设您实际上已集成到ASP.NET Core身份验证子系统(即services.AddAuthentication
和app.UseAuthentication
)中,那么这基本上是为您处理的。将读取JWT以构建一个ClaimsPrincipal
实例,然后将其存储在HttpContext.User
中。因此,用户的用户名将位于HttpContext.User.Identity.Name
的标准位置,或者您可以通过Claims
上的HttpContext.User.Identity
集合直接访问(以及任何其他声明)。
如果问题是您需要将此信息放在您无权直接访问HttpContext.User
的地方(基本上是控制器或视图之外的任何地方),那么您只需要注入IHttpContextAccessor
。这需要两件事:
您必须添加IHttpContextAccessor
服务。出于性能原因,默认情况下不包括此内容。 (这并不是说它会对性能产生严重影响。只是如果你不需要它,你可以通过不包括它来获得更多的性能.ASP.NET Core只包含你的内容需要包括。)无论如何:
ASP.NET Core 2.1
services.AddHttpContextAccessor();
以前的版本
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
无论您何时注入,都需要成为请求管道的一部分,否则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);
}
}