- 使用扩展方法更改ASP.NET Core Identity用户密
- asp.net core系列 46 Identity介绍
- asp.net core系列 48 Identity 身份模型自
- gRPC中集成asp.net identity实现oAuth认证
- IdentityServer4 第二部分 快速入门第16章 使用
- asp.net core 使用 signalr(二)
- asp.net core 使用 signalr(一)
- asp.net core 3.0 中使用 swagger
- ASP.NET Core Swagger接入使用Identity
- 《从零开始学ASP.NET CORE MVC》: ASP.NET
ASP.NET Core Identity提供的UserManager包括了一个修改密码的方法:
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult> ChangePasswordAsync (TUser user, string currentPassword, string newPassword);
方法的参数分别是要修改密码的用户对象、当前密码和修改后的密码。很显然,这个方法能够满足常见的用户修改自己密码的场景:用户在UI中输入当前密码和新的密码,业务逻辑验证当前密码正确,然后用新的密码替换数据库中的当前密码。
但是这个方法无法满足这种场景:我是系统管理员,我不知道用户的当前密码,但我需要重置或修改这个用户的密码。因为我无法提供用户的当前密码,就没法使用上面的ChangePasswordAsync方法。
为了实现这个需求,查看UserManager的文档,ChangePasswordAsync方法并没有我们需要的重载,必须得提供当前密码才行,所以只能放弃这个方法,另僻蹊径。
浏览了整个UserManager类之后,没有找到想要的方法,但是看到这两个方法:AddPasswordAsync和RemovePasswordAsync,想到了一种解决方式:先移除旧密码,再添加新密码,效果和更改密码是一样的。
if (changedPassword)
{
//如果用户修改了密码,先移除该用户的密码,再为该用户添加新的密码
await userManager.RemovePasswordAsync(updateUser);
await userManager.AddPasswordAsync(updateUser, newPassword);
}
测试之后,效果和修改密码相同,而且不用提供用户的当前密码。
看起来没有任何问题了,但是等等!如果我们的代码运行完RemovePasswordAsync之后,系统因为各种原因意外崩溃了,那么用户的密码已经被删除,但新的密码却没有添加……也就是说,除非我们能保证这两个方法要么全部执行,要么全部不执行,就象数据库中的事务一样,否则这个修改密码的业务逻辑就存在一个很大的漏洞。
UserManager封装了对数据库的操作,显然我们没有办法为这两个方法添加“事务”,这种方式也得放弃。
下面我们继续查看UserManager文档,找到了属性Store,一个用于持久化存储的管理器,这个属性的类型接口IUserStore<TUser>,接口中定义了UpdateAsync方法用于更新用户,而且更新的是持久化存储,也就是数据库。但是这个接口中并没有提供和密码有关的方法。但是IUserStore派生出众多的子接口,其中有一个IUserPasswordStore<TUser>包含了三个方法:GetPasswordAsync,HasPasswordAsync和SetPasswordAsync。根据子类可以替换父类的原则,Store属性也可以是IUserPasswordStore的实现类对象。因为文档中无法体现这一点,我们需要用代码验证一下,简单地说,就是在代码中测试一下Store属性是否是IUserPasswordStore的实例:检查userManager.Store is IUserPasswordStore<TUser>返回值。这个很简单,我们在任意一个使用了UserManager对象的代码段中加一行代码测试一下就行了。不过问题又来了,Store是一个受保护的属性,无法直接访问,需要使用反射。
当然最终的结果肯定是符合我们期望的,不然就不会有这篇文章了,最后我们为UserManager添加一个扩展方法,实现我们的目的,上代码:
public static class UserManagerExtensions
{
/// <summary>
/// 修改用户密码,无需提供用户当前密码
/// </summary>
/// <param name="userManager"></param>
/// <param name="user">要修改密码的用户对象</param>
/// <param name="password">修改后的密码</param>
/// <returns></returns>
public static async Task<IdentityResult> ChangePasswordAsync(this UserManager<User> userManager, User user, string password)
{
var type = userManager.GetType();
var storeProperty = type.GetProperty("Store", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var store = storeProperty.GetValue(userManager) as IUserPasswordStore<User>;
if (store == null)
{
return IdentityResult.Failed(new IdentityError { Code = "NotImplements", Description = "持久化存储器没有实现IUserPasswordStore接口" });
}
var passwordHash = userManager.PasswordHasher.HashPassword(user, password);
await store.SetPasswordHashAsync(user, passwordHash, System.Threading.CancellationToken.None);
await store.UpdateAsync(user, System.Threading.CancellationToken.None);
return IdentityResult.Success;
}
}
代码中的User类是自定义的用户类,继承自IdentityUser类。
调用:
if (changedPassword)
{
await userManager.ChangePasswordAsync(updateUser, newPassword);
}
网友评论