如何覆盖 ASP.NET Core Identity 的密码策略

Posted

技术标签:

【中文标题】如何覆盖 ASP.NET Core Identity 的密码策略【英文标题】:How override ASP.NET Core Identity's password policy 【发布时间】:2017-02-11 01:05:41 【问题描述】:

默认情况下,ASP.NET Core Identity 的密码策略要求至少有一个特殊字符、一个大写字母、一个数字……

如何更改此限制?

文档中对此一无所知 (https://docs.asp.net/en/latest/security/authentication/identity.html)

我尝试覆盖身份的用户管理器,但我没有看到管理密码策略的方法。

public class ApplicationUserManager : UserManager<ApplicationUser>

    public ApplicationUserManager(
        DbContextOptions<SecurityDbContext> options,
        IServiceProvider services,
        IHttpContextAccessor contextAccessor,
        ILogger<UserManager<ApplicationUser>> logger)
        : base(
              new UserStore<ApplicationUser>(new SecurityDbContext(contextAccessor)),
              new CustomOptions(),
              new PasswordHasher<ApplicationUser>(),
              new UserValidator<ApplicationUser>[]  new UserValidator<ApplicationUser>() ,
              new PasswordValidator[]  new PasswordValidator() ,
              new UpperInvariantLookupNormalizer(),
              new IdentityErrorDescriber(),
              services,
              logger
            // , contextAccessor
              )
    
    

    public class PasswordValidator : IPasswordValidator<ApplicationUser>
    
        public Task<IdentityResult> ValidateAsync(UserManager<ApplicationUser> manager, ApplicationUser user, string password)
        
            return Task.Run(() =>
            
                if (password.Length >= 4) return IdentityResult.Success;
                else  return IdentityResult.Failed(new IdentityError  Code = "SHORTPASSWORD", Description = "Password too short" ); 
            );
        
    

    public class CustomOptions : IOptions<IdentityOptions>
    
        public IdentityOptions Value  get; private set; 
        public CustomOptions()
        
            Value = new IdentityOptions
            
                ClaimsIdentity = new ClaimsIdentityOptions(),
                Cookies = new IdentityCookieOptions(),
                Lockout = new LockoutOptions(),
                Password = null,
                User = new UserOptions(),
                SignIn = new SignInOptions(),
                Tokens = new TokenOptions()
            ;
        
    

我在启动类中添加了这个用户管理器依赖:

services.AddScoped<ApplicationUserManager>();

但是当我在控制器中使用 ApplicationUserManager 时,出现错误: 处理请求时发生未处理的异常。

InvalidOperationException:尝试激活“ApplicationUserManager”时,无法解析“Microsoft.EntityFrameworkCore.DbContextOptions`1[SecurityDbContext]”类型的服务。

编辑:当我使用 ASP.NET Core Identity 的默认类时,用户的管理工作正常,所以这不是数据库问题,或类似的问题

编辑 2:我找到了解决方案,您只需在启动类中配置 Identity。我的回答提供了一些细节。

【问题讨论】:

有趣的事实:MS 强加的默认设置对于 chrome 密码管理器生成的密码来说太严格了。 【参考方案1】:

最后太简单了……

无需覆盖任何类,您只需在启动类中配置身份设置,如下所示:

services.Configure<IdentityOptions>(options =>

    options.Password.RequireDigit = false;
    options.Password.RequiredLength = 5;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonLetterOrDigit = true;
    options.Password.RequireUppercase = false;
);

或者你可以在添加的时候配置身份:

services.AddIdentity<ApplicationUser, IdentityRole>(options=> 
                options.Password.RequireDigit = false;
                options.Password.RequiredLength = 4;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequireLowercase = false;
            )
                .AddEntityFrameworkStores<SecurityDbContext>()
                .AddDefaultTokenProviders();

AS.NET Core 绝对是好东西 ...

【讨论】:

解决方案添加到官方文档docs.asp.net/en/latest/security/authentication/identity.html 应用您的解决方案,看起来在 ASP .Net Core 中选项 options.Password.RequireNonAlphanumeric = false; 已被弃用并分为 2 个“子选项”:options.Password.RequireDigit = false;options.Password.RequireNonAlphanumeric = false; 有趣,我想允许调试时密码长度为 1,但 options.Password.RequiredLength = 1;不起作用(尝试了上述两种情况)。验证仍然需要 6 个字符。其他选项确实有效。 这不起作用。我将顶部添加到 startup.cs 中的 ConfigureServices 方法的底部,我将 RequiredLength 设置为 1,但错误仍然指出它必须在 6 到 100 个字符之间。 (.net 核心 3.2) RequiredLength github.com/dotnet/aspnetcore/blob/…。如果您可以通过它,它似乎确实尊重其他设置。例如,如果您将 RequiredLength 设置为 7 并且只输入 6 个字符,它将通过该字段的最少 6 个检查,然后在您点击注册按钮时失败并显示“需要 7”。【参考方案2】:

您可以在 IdentityConfig.cs 文件中修改这些规则。 规则在

中定义
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)

    var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
    // Configure validation logic for usernames
    manager.UserValidator = new UserValidator<ApplicationUser>(manager)
    
        AllowOnlyAlphanumericUserNames = false,
        RequireUniqueEmail = true
    ;

    // Configure validation logic for passwords
    manager.PasswordValidator = new PasswordValidator
    
        RequiredLength = 5,
        RequireNonLetterOrDigit = false,
        RequireDigit = true,
        RequireLowercase = true,
        RequireUppercase = true,
    ;

【讨论】:

【参考方案3】:

附加要求:

如果你觉得这个密码限制还不够,你可以定义你的 通过继承自己的条件 PasswordValidator 类。

示例实现:

public class CustomPasswordPolicy : PasswordValidator<AppUser>
    
        public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
        
            IdentityResult result = await base.ValidateAsync(manager, user, password);
            List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();

            if (password.ToLower().Contains(user.UserName.ToLower()))
            
                errors.Add(new IdentityError
                
                    Description = "Password cannot contain username"
                );
            
            if (password.Contains("123"))
            
                errors.Add(new IdentityError
                
                    Description = "Password cannot contain 123 numeric sequence"
                );
            
            return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
        
    

我已经在我的类中重写了 ValidateAsync 方法,并且在这个方法中我正在实现我的自定义密码策略。

非常非常重要

ValidateAsync() 中的第一行代码

IdentityResult result = await base.ValidateAsync(manager, user, password);

根据 Statup 类的 ConfigureServices 方法中给出的密码规则验证密码(本文旧答案中显示的规则)

密码验证功能由 Microsoft.AspNetCore.Identity 命名空间中的 IPasswordValidator 接口。所以我需要将我的“CustomPasswordPolicy”类注册为“AppUser”对象的密码验证器。
    services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordPolicy>();
            services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
            services.AddIdentity<AppUser, IdentityRole>(opts =>
            
                opts.Password.RequiredLength = 8;
                opts.Password.RequireNonAlphanumeric = true;
                opts.Password.RequireLowercase = false;
                opts.Password.RequireUppercase = true;
                opts.Password.RequireDigit = true;
            ).AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();

PasswordValidator.cs 的官方 Github 文档(为了更好 理解):here

【讨论】:

您好,注册时还没有关联用户怎么办?【参考方案4】:

开发人员最简单的方法是

services.AddDefaultIdentity<IdentityUser>(options =>

  options.SignIn.RequireConfirmedAccount = true;
  options.Password.RequireDigit = false;
  options.Password.RequireNonAlphanumeric = false;
  options.Password.RequireUppercase = false;
  options.Password.RequireLowercase = false;
)
  .AddEntityFrameworkStores<ApplicationDbContext>();

只有Password.RequiredLength不能这样改,还是等于6。

【讨论】:

【参考方案5】:

将以下行添加到 startup.cs 的 ConfigureServices 方法中

services.Configure<IdentityOptions>(Configuration.GetSection(nameof(IdentityOptions)));

如果需要,您可以使用不同的部分名称

然后将设置添加到 config.您可以在多个配置源中添加多个设置,它们将被合并。 例如。我把它放在我的 appsettings.local.json 文件中。此文件被 VCS 忽略,因此我的本地设置永远不会生效,这与硬编码设置并使用 #if debug 或类似的东西不同。

"IdentityOptions": 
"Password": 
  "RequiredLength": 6,
  "RequireDigit": false,
  "RequiredUniqueChars": 1,
  "RequireLowercase": false,
  "RequireNonAlphanumeric": false,
  "RequireUppercase": false
 

这同样适用于 appsettings.Environment.json 或任何其他配置源,因此您可以在开发服务器和实时服务器上进行不同的设置,而无需更改代码或使用不同的构建配置

【讨论】:

以上是关于如何覆盖 ASP.NET Core Identity 的密码策略的主要内容,如果未能解决你的问题,请参考以下文章

如何使用环境变量覆盖 ASP.NET Core 配置数组设置

ASP.NET Core 2.1 显示用户和角色

ASP.NET Core运行两个TestServer进行集成测试

覆盖 asp.net core razor 页面中的 razor 视图

路由覆盖在 asp.net core 3.1 剃须刀页面中不起作用

小5聊Asp.Net Core基础之发布不成功或者覆盖IIS上的DLL提示程序正在打开