You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为不同用户动态配置ASP.NET Identity的MaxFailedAccessAttempts

动态配置ASP.NET Identity用户专属的MaxFailedAccessAttempts

要实现不同用户拥有不同的登录失败锁定阈值,且值存储在缓存中,核心是重写Identity框架中获取最大失败尝试次数的逻辑,让其优先从缓存读取用户专属配置,再 fallback 到全局默认值。以下是具体实现步骤:

1. 自定义UserLockoutStore,重写阈值获取方法

Identity默认通过UserLockoutStore的GetMaxFailedAccessAttemptsAsync方法获取全局配置的阈值,我们需要继承默认的UserStore并重写该方法,改为从缓存读取用户专属值:

public class CustomUserLockoutStore : UserStore<ApplicationUser, Role, ApplicationDbContext, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>
{
    private readonly IDistributedCache _cache;
    private readonly IOptions<IdentityOptions> _identityOptions;

    public CustomUserLockoutStore(ApplicationDbContext context, IDistributedCache cache, IOptions<IdentityOptions> identityOptions)
        : base(context)
    {
        _cache = cache;
        _identityOptions = identityOptions;
    }

    public override async Task<int> GetMaxFailedAccessAttemptsAsync(ApplicationUser user, CancellationToken cancellationToken = default)
    {
        // 构造缓存Key,格式示例:User:Lockout:用户ID
        var cacheKey = $"User:Lockout:{user.Id}";
        var cachedBytes = await _cache.GetAsync(cacheKey, cancellationToken);

        if (cachedBytes != null && int.TryParse(Encoding.UTF8.GetString(cachedBytes), out int userAttempts))
        {
            return userAttempts;
        }

        // 缓存无值时,返回全局默认配置
        return _identityOptions.Value.Lockout.MaxFailedAccessAttempts;
    }
}

2. 替换默认的UserStore到DI容器

在Program.cs(或Startup.cs)中注册Identity时,替换默认的用户存储为我们自定义的CustomUserLockoutStore:

// 保留原Identity配置,全局默认值作为兜底
services.AddIdentity<ApplicationUser, Role>(config =>
{
    config.Lockout.MaxFailedAccessAttempts = 3; // 可设为经理用户默认值,或通用兜底值
    config.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(24);
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddUserStore<CustomUserLockoutStore>(); // 替换为自定义存储

3. 登录时加载用户专属配置到缓存

在登录逻辑中,先从数据源(比如数据库用户表的自定义字段)读取该用户的阈值,存入缓存后再执行登录验证:

public async Task<IActionResult> Login(LoginViewModel model)
{
    if (!ModelState.IsValid) return View(model);

    var user = await _userManager.FindByNameAsync(model.UserName);
    if (user == null)
    {
        ModelState.AddModelError("", "无效的登录信息");
        return View(model);
    }

    // 假设用户表有自定义字段MaxFailedAccessAttempts,从数据库读取后存入缓存
    var userAttempts = user.MaxFailedAccessAttempts; // 示例:经理用户3次,普通用户5次
    var cacheKey = $"User:Lockout:{user.Id}";
    await _cache.SetStringAsync(cacheKey, userAttempts.ToString(), new DistributedCacheEntryOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24) // 设置缓存有效期
    });

    // 执行登录验证,此时Identity会调用自定义Store的方法获取阈值
    var result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: true);
    if (result.Succeeded)
    {
        return RedirectToAction("Index", "Home");
    }
    else if (result.IsLockedOut)
    {
        ModelState.AddModelError("", "账号已锁定,请24小时后重试");
        return View(model);
    }
    else
    {
        ModelState.AddModelError("", "登录失败,请检查用户名或密码");
        return View(model);
    }
}

补充说明

  • 若用户的阈值需要更新,只需修改缓存对应Key的值,下次验证时会自动生效;
  • 缓存过期后会自动 fallback 到全局默认值,也可以在用户资料修改逻辑中同步更新缓存;
  • 该方案覆盖了Identity所有涉及锁定阈值的场景(如密码错误计数、锁定判断),无需修改多处逻辑。

内容的提问来源于stack exchange,提问作者jkamz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:07:53