AspNet Identity Core - 登录时的自定义声明
Posted
技术标签:
【中文标题】AspNet Identity Core - 登录时的自定义声明【英文标题】:AspNet Identity Core - Custom Claims on Login 【发布时间】:2019-04-22 02:21:09 【问题描述】:我正在尝试通过在数据库中添加一个逻辑“已删除”列来扩展我的身份用户。
然后我想使用此值通过自定义 UserClaimsPrincipalFactory 向用户添加声明。
我想在登录时检查“已删除”声明,如果用户的帐户已被删除,则拒绝该用户。
问题:当我尝试通过 User.Claims 访问声明时,用户没有声明。
我唯一能让它工作的方法是覆盖 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...
我的 ApplicationUser 类
public class ApplicationUser : IdentityUser
public bool Deleted get; set;
最后是我的启动注册
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddClaimsPrincipalFactory<ApplicationClaimsIdentityFactory>()
.AddDefaultTokenProviders();
谢谢
【问题讨论】:
【参考方案1】:我认为问题是您在发布登录操作时试图在一个请求中完成所有操作。
您在该方法中拥有的用户是 claimprincipal 但未经过身份验证,在调用 SignIn 的代码之前和调用您的 claimprincipal 工厂方法之前,它已由 auth 中间件从请求中反序列化。
signin 方法确实创建了新的经过身份验证的 claimprincipal 并且应该将其序列化到 auth cookie 中,因此在下一个请求中,用户将从 cookie 中反序列化并进行身份验证,但是当前请求已经发生了反序列化。因此,为当前请求更改它的唯一方法是在您找到的当前 httpcontext 上重置用户。
我认为最好不要在登录成功后以不同的方式拒绝用户,最好在较低级别进行检查并使登录失败,而不是成功然后注销。我在我的项目中的自定义用户存储中做到了这一点。
【讨论】:
谢谢 - 这确实有助于说明我哪里出错了。我已经实现了自己的 SignInManager CanSignInAsync 方法并在该级别进行了处理。【参考方案2】:在 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);
这是我的第一个堆栈溢出答案:)
【讨论】:
以上是关于AspNet Identity Core - 登录时的自定义声明的主要内容,如果未能解决你的问题,请参考以下文章
将现有 Microsoft.AspNet.Identity DB (EF 6) 迁移到 Microsoft.AspNetCore.Identity (EF Core)
Aspnet Core 3.1 Identity ConfirmEmail 未按预期工作
AspNet Core Identity,如何设置options.Cookie.SameSite?
将 Microsoft.AspNet.Identity 升级到 rc1 后找不到 IdentityStoreManager