我的公司计划将我们的应用程序从.NET Framework升级到.NET Core,并将其从ASP.NET Membership升级到ASP.NET Core Identity server。我在这里找到了一篇有用的文章。
然而,有一个具有重大影响的子注释:
完成此脚本后,ASP.NET Core Identity应用程序创建早期版本中填充了Membership用户。用户需要更改登录前的密码。
作为迁移的一部分,我们不能要求60万用户更改密码。然而,成员密码是单向散列的,所以我们不能检索它们然后迁移它们。因此,我想知道我们将如何使用新的Identity Server方法来维护现有用户的密码。
我最近才这么做。
我们有一个遗留的.net会员系统,需要将大约1万用户导入asp.net身份。当我从系统中复制所有用户时,我首先在asp.net标识核心用户表中创建了一个额外的列,并随身携带了他们的旧密码。
然后当用户第一次登录时。我首先检查了遗留密码是否存在,如果存在的话,然后我根据这个验证了它们,并在asp上更新了密码。net身份核心,并删除了遗留密码。通过这种方式,所有用户都在没有意识到的情况下将密码移植到新系统
我将尝试解释我是如何做到的,但代码有点疯狂。
实际上,我在应用程序用户表中添加了两列
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!
我们最近从各种遗留系统迁移,由于它们都使用了各种形式的哈希密码,我们没有试图通过端口传递该逻辑,而是定制了密码验证代码,使其能够调用每个遗留系统公开的API。从这样的系统迁移的每个用户都存储了API URL
当迁移的用户第一次登录时,我们会调用所述服务(该服务本身使用承载令牌和受限的集成范围进行保护(,以便第一次进行密码验证。如果我们得到了成功的响应,那么我们就用自己的格式对密码进行散列,这样就会永远使用下去。
这样做的缺点是,您必须让旧系统(使用这个新的API(几乎永远保持运行。由于它都是.Net,所以如果您可以在.Net Core中运行旧哈希方案的实现,那么通过将其全部保持在进程中并将迁移的用户哈希密码复制到新的DB中,您可能会做得更好。