【问题标题】:Maintain passwords while switching from ASP.NET Membership to ASP.NET Core Identity从 ASP.NET 成员身份切换到 ASP.NET Core 身份时维护密码
【发布时间】:2019-05-08 15:40:46
【问题描述】:

我的公司正计划将我们的应用程序从 .NET Framework 升级到 .NET Core,并作为其中的一部分从 ASP.NET Membership 升级到 ASP.NET Core Identity 服务器。我在here 上找到了一篇有用的文章。

但是,有一个包含大量含义的子注释:

完成此脚本后,已创建 ASP.NET Core Identity 应用 较早时填充了会员用户。用户需要改变他们的 登录前的密码。

作为迁移的一部分,我们不能要求 600,000 名用户更改密码。但是,会员密码是单向散列的,因此我们无法检索它们然后迁移它们。所以我想知道我们将如何使用新的 Identity Server 方法来维护现有用户的密码。

【问题讨论】:

  • 您使用的是什么加密方式?表单身份验证?
  • 是的,这在传统上用于具有基于 cookie 的表单身份验证的旧 ASP.NET Web 表单应用程序。
  • 我们遇到了同样的问题。我们使用的是 FormsAuthentication,不幸的是它在 .NET Core 中已经过时了。我的同事写这个是为了让它在 Core 中工作:github.com/synercoder/FormsAuthentication
  • 我不担心未来支持表单身份验证,我们将停用此 Web 应用程序并改用 AngularJs 应用程序。我担心迁移用户的密码以便他们在 Identity Server 中工作。
  • 看起来您需要找到会员密码哈希算法的源代码,并将其作为IPasswordHasher<TUser> 实现拉入您的新项目。我建议将旧密码哈希存储在单独的列中,以便您可以在使用旧算法验证任何现有密码后将其重新哈希为新格式。

标签: asp.net-core asp.net-identity asp.net-membership identityserver4


【解决方案1】:

我最近才这样做。

我们有一个旧的 .net 会员系统,需要将大约 10k 用户导入到 asp.net 身份。当我从系统中复制所有用户时,我首先在 asp .net 身份核心用户表中创建了一个额外的列,我带来了他们的旧密码。

然后当用户第一次登录时。我首先检查旧密码是否存在,如果存在,然后我验证它们并更新 asp 上的密码。 net 身份核心并删除了旧密码。这样一来,所有用户都将他们的密码移植到了新系统上,甚至都没有意识到。

我将尝试解释我是如何做到的,但代码有点疯狂。

我实际上在 applicationuser 表中添加了两列

public string LegacyPasswordHash { get; set; }
public string LegacyPasswordSalt { get; set; }

ApplicationSignInManager -> CheckPasswordSignInAsync 方法检查用户是否是旧用户

ApplicationSignInManager

public override async Task<SignInResult> CheckPasswordSignInAsync(ApplicationUser user, string password, bool lockoutOnFailure)
        {
        ........

            if (user.IsLegacy)
            {
                Logger.LogDebug(LoggingEvents.ApplicationSignInManagerCheckPasswordSignInAsync, "[user.Id: {user.Id}] is legacy.", user.Id);
                var results = await new LoginCommand(_logger, _userManager, user, password, lockoutOnFailure).Execute();
                if (results.Succeeded)
                {
                    await ResetLockout(user);
                    return SignInResult.Success;
                }
            }
            else if (await UserManager.CheckPasswordAsync(user, password))
            {
                await ResetLockout(user);
                return SignInResult.Success;
            }

            ........
        }

登录命令

 public class LoginCommand
    {
        private readonly ILogger _logger;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly ApplicationUser _user;
        private readonly string _password;
        private readonly bool _shouldLockout;

        public LoginCommand(ILogger logger, UserManager<ApplicationUser> userManager, ApplicationUser user, string password, bool shouldLockout)
        {
            _logger = logger;
            _userManager = userManager;
            _user = user;
            _password = password;
            _shouldLockout = shouldLockout;
        }

        public async Task<SignInResult> Execute()
        {
            _logger.LogInformation($"Found User: {_user.UserName}");
            if (_user.IsLegacy)
                return await new LegacyUserCommand(_logger, _userManager, _user, _password, _shouldLockout).Execute();
            if (await _userManager.CheckPasswordAsync(_user, _password))
                return await new CheckTwoFactorCommand(_logger, _userManager, _user).Execute();
            if (_shouldLockout)
            {
                return await new CheckLockoutCommand(_logger, _userManager, _user).Execute();
            }
            _logger.LogDebug($"Login failed for user {_user.Email} invalid password");
            return SignInResult.Failed;
        }
    }

LegacyUserCommand

  public class LegacyUserCommand
    {
        private readonly ILogger _logger;
        private readonly UserManager<ApplicationUser> _userManager;

        private readonly ApplicationUser _user;
        private readonly string _password;
        private bool _shouldLockout;

        public LegacyUserCommand(ILogger logger, UserManager<ApplicationUser> userManager, ApplicationUser user, string password, bool shouldLockout)
        {
            _logger = logger;
            _userManager = userManager;
            _user = user;
            _password = password;
            _shouldLockout = shouldLockout;
        }

        public async Task<SignInResult> Execute()
        {
            try
            {
                if (_password.EncodePassword(_user.LegacyPasswordSalt) == _user.LegacyPasswordHash)
                {
                    _logger.LogInformation(LoggingEvents.LegacyUserCommand, "Legacy User {_user.Id} migrating password.", _user.Id);
                    await _userManager.AddPasswordAsync(_user, _password);
                    _user.SecurityStamp = Guid.NewGuid().ToString();
                    _user.LegacyPasswordHash = null;
                    _user.LegacyPasswordSalt = null;
                    await _userManager.UpdateAsync(_user);
                    return await new CheckTwoFactorCommand(_logger, _userManager, _user).Execute();
                }
                if (_shouldLockout)
                {
                    _user.SecurityStamp = Guid.NewGuid().ToString();
                    await _userManager.UpdateAsync(_user);
                    _logger.LogInformation(LoggingEvents.LegacyUserCommand, "Login failed for Legacy user {_user.Id} invalid password. (LockoutEnabled)", _user.Id);
                    await _userManager.AccessFailedAsync(_user);
                    if (await _userManager.IsLockedOutAsync(_user))
                        return SignInResult.LockedOut;
                }

                _logger.LogInformation(LoggingEvents.LegacyUserCommand, "Login failed for Legacy user {_user.Id} invalid password", _user.Id);
                return SignInResult.Failed;
            }
            catch (Exception e)
            {
                _logger.LogError(LoggingEvents.LegacyUserCommand, "LegacyUserCommand Failed for [_user.Id: {_user.Id}]  [Error Message: {e.Message}]", _user.Id, e.Message);
                _logger.LogTrace(LoggingEvents.LegacyUserCommand, "LegacyUserCommand Failed for [_user.Id: {_user.Id}] [Error: {e}]", _user.Id, e);
                return SignInResult.Failed;
            }
        }
    }

重要提示:[SecurityStamp] 不能为 NULL!

【讨论】:

  • 那么您创建的额外列是为他们的旧哈希密码?您究竟是如何验证旧密码的(代码会有所帮助)?另外,当您不知道实际密码是什么时(它是单向哈希),您是如何更新身份核心中的密码的?
  • 1.哈希和盐 2. 用户在他们的登录名中输入密码,密码以明文形式提交,您只需保存即可。 3. 我希望这有点道理。让我知道。 4.
  • 您有可以发布的 EncodePassword 代码吗?这似乎是最重要的部分!
【解决方案2】:

我们最近从各种遗留系统迁移,因为它们都使用各种形式的哈希密码,而不是尝试移植该逻辑,我们定制了密码验证代码,以允许它调用由每个遗留系统。从这样的系统迁移的每个用户都有针对它存储的 API URL。

当迁移的用户首次登录时,我们会调用该服务(该服务本身使用不记名令牌和受限集成范围进行保护)以在第一次进行密码身份验证。如果我们得到一个成功的响应,那么我们会以我们自己的格式对密码进行哈希处理,并且会一直使用下去。

这样做的缺点是您必须几乎永远保持旧系统(使用此新 API)。既然都是 .Net,你可能会更好地保持它全部在进程中并将迁移的用户散列密码复制到你的新数据库,假设你可以在 .Net Core 中运行旧的散列方案的实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 2013-09-06
    • 1970-01-01
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多