AspNet身份核心-登录时的自定义声明



我正试图通过在数据库中添加逻辑"已删除"列来扩展我的身份用户。

然后,我想使用这个值向使用自定义UserClaimsPrincipalFactory的用户添加声明。

我想在登录时检查"已删除"声明,如果用户的帐户已被删除,则拒绝该用户。

问题:当我尝试通过User访问声明时。声明用户没有声明。

我唯一能让它工作的方法是覆盖httpcontext用户

public class ApplicationClaimsIdentityFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
private readonly IHttpContextAccessor _httpContext;
public ApplicationClaimsIdentityFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options, IHttpContextAccessor httpContext) : base(userManager, roleManager, options)
{
_httpContext = httpContext;
}
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
ClaimsPrincipal principal = await base.CreateAsync(user);
ClaimsIdentity claimsIdentity = (ClaimsIdentity) principal.Identity;
claimsIdentity.AddClaim(new Claim("Deleted", user.Deleted.ToString().ToLower()));
//I DON'T WANT TO HAVE TO DO THIS
_httpContext.HttpContext.User = principal;

return principal;
}
}

登录操作:

public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
SignInResult result = await _signInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//No claims exists at this point unless I force the HttpContext user (See above)
if (User.Claims.First(x => x.Type == "Deleted").Value.Equals("true", StringComparison.CurrentCultureIgnoreCase);)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
await _signInManager.SignOutAsync();
return View(model);
}
.... Continue login code...

我的应用程序用户类

public class ApplicationUser : IdentityUser
{
public bool Deleted { get; set; }
}

最后我的启动注册

services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddClaimsPrincipalFactory<ApplicationClaimsIdentityFactory>()
.AddDefaultTokenProviders();

感谢

我认为问题在于,当发布登录操作时,您试图在一个请求中完成所有操作。

该方法中的User是claimsprincipal,但未通过身份验证,在调用SignIn的代码之前以及调用claimspricipal工厂方法之前,它是由身份验证中间件从请求中反序列化的。

signin方法确实创建了新的经过身份验证的claimsprincipal,并且应该将其序列化到auth cookie中,因此在下一个请求中,用户将从cookie中反序列化并进行身份验证,但当前请求已经进行了反序列化。因此,为当前请求更改它的唯一方法是重置当前httpcontext上的User,正如您所发现的那样。

我认为最好以不同的方式拒绝用户,而不是在登录成功后,最好在较低级别检查并使登录失败,而不是成功后注销。我在一个自定义用户商店的项目中做到了这一点。

声明在PasswordSignInAsync方法之后不可用。一种变通方法是,您可以使用它在构造函数中添加UserManager,如:

private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<LoginModel> _logger;
private readonly UserManager<ApplicationUser> _userManager; //<----here
public LoginModel(SignInManager<ApplicationUser> signInManager, 
UserManager<ApplicationUser> userManager, //<----here
ILogger<LoginModel> logger)
{
_signInManager = signInManager;
_userManager = userManager;//<----here
_logger = logger;
}

在Login方法中,您可以检查IsDeleted属性,如:

var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
var user =  await _userManager.FindByNameAsync(Input.Email); //<----here
if (user.IsDeleted)  //<----here
{
ModelState.AddModelError(string.Empty, "This user doesn't exist.");
await _signInManager.SignOutAsync();
return Page();
}

_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}

这是我的第一个堆栈溢出答案:(

使用这两种方法之一。我测试了这些。

IHttpContextAccessor httpContext添加到UserClaimsPrincipalFactory构造函数中。

var newClaims = new Claim[] { new ("type1", "value1"), new ("type2", "value2")};
claimsIdentity.AddClaims(newClaims);

然后将您的自定义声明添加到_httpContext:

_httpContext.HttpContext.User.AddIdentity(claimsIdentity);

同时,您可以通过以下方式将值设置为Items["harchi"]

_httpContext.HttpContext.Items["enything"] = value;

然后使用这个:

HttpContext.Items["harchi"];