Abp Core 添加短信验证码登录(动态密码登录)
现目前我国网站的已经很少使用电子邮箱了,基本上都是手机号作为账号,有时候粗心的用户会忘记密码,网站就提供了更方便的短信验证码登录.使用短信验证码来替代密码,本小白也想实现这个功能,但是Abp并没有提供这样的方法,于是经过不懈努力,终于搞定了,废话不多说下面上代码.
1.添加通过手机号查询用户方法
在Core\Authorization\Users\UserStore.cs
/// <summary>
/// 通过手机号查询用户
/// </summary>
/// <param name="phoneNumber">手机号</param>
/// <returns></returns>
public virtual async Task<User> FindByPhoneNumberAsync(string phoneNumber)
{
return await _userRepository.FirstOrDefaultAsync(model => model.PhoneNumber == phoneNumber);
}
/// <summary>
/// 通过手机号查询用户
/// </summary>
/// <param name="tenantId">租户id</param>
/// <param name="phoneNumber">手机号</param>
/// <returns></returns>
[UnitOfWork]
public virtual async Task<User> FindByPhoneNumberAsync(int? tenantId, string phoneNumber)
{
using (_unitOfWorkManager.Current.SetTenantId(tenantId))
{
return await FindByPhoneNumberAsync(phoneNumber);
}
}
2.添加验证码效验方法
在Core\Authorization\Users\UserManager.cs
/// <summary>
/// 检查验证码
/// </summary>
/// <param name="user">用户</param>
/// <param name="captcha">验证码</param>
/// <returns></returns>
public virtual async Task<bool> CheckCaptcha(User user, int captcha)
{
var entity = await _smsCaptchaRepository.FirstOrDefaultAsync(model => model.PhoneNumer == user.PhoneNumber);
if (entity.Captcha == captcha)
return true;
return false;
}
3.添加手机验证码登录功能
在Core\Authorization\LoginManager.cs
手机验证码登陆
/// <summary>
/// 手机验证码登陆
/// </summary>
/// <param name="phoneNumber">手机号</param>
/// <param name="captcha">验证码</param>
/// <param name="tenantName">租户名</param>
/// <param name="shouldLockout">是否锁定</param>
/// <returns></returns>
public virtual async Task<AbpLoginResult<Tenant, User>> LoginByMobileAsync(string phoneNumber, int captcha,
string tenantName = null, bool shouldLockout = true)
{
var result = await LoginByMobileAsyncInternal(phoneNumber, captcha, tenantName, shouldLockout);
await SaveLoginAttempt(result, tenantName, result.User.UserName);
return result;
}
手机验证码登陆内部方法
/// <summary>
/// 手机验证码登陆内部方法
/// </summary>
/// <param name="phoneNumber">手机号</param>
/// <param name="captcha">验证码</param>
/// <param name="tenantName">租户名</param>
/// <param name="shouldLockout">是否锁定</param>
/// <returns></returns>
protected virtual async Task<AbpLoginResult<Tenant, User>> LoginByMobileAsyncInternal(string phoneNumber,
int captcha, string tenantName, bool shouldLockout)
{
if (phoneNumber.IsNullOrEmpty())
{
Logger.Error("手机号不能为空");
throw new UserFriendlyException("手机号不能为空");
}
//获取和检查租户
Tenant tenant = null;
//设置当前租户id为空
using (_unitOfWorkManager.Current.SetTenantId(null))
{
//如果关闭租户就获取默认租户
if (!_multiTenancyConfig.IsEnabled)
{
//获取默认租户
tenant = await GetDefaultTenantAsync();
}
//租户id是否为空
else if (!string.IsNullOrWhiteSpace(tenantName))
{
//查询租户信息
tenant = await _tenantRepository.FirstOrDefaultAsync(model => model.TenancyName == tenantName);
if (tenant == null)
//返回无效租户
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.InvalidTenancyName);
if (!tenant.IsActive)
//返回租户未启用
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.TenantIsNotActive, tenant);
}
}
//设置租户id
var tenantId = tenant == null ? (int?)null : tenant.Id;
//设置当前租户id
using (_unitOfWorkManager.Current.SetTenantId(tenantId))
{
//初始化选项
await _userManager.InitializeOptionsAsync(tenantId);
var loggedInFromExternalSource = false;
//通过手机号查询用户
var user = await _userStore.FindByPhoneNumberAsync(tenantId, phoneNumber);
if (user == null)
//返回无效用户
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.InvalidUserNameOrEmailAddress, tenant);
//用户是否被锁定
if (await _userManager.IsLockedOutAsync(user))
//返回锁定用户
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.LockedOut, tenant, user);
if (!loggedInFromExternalSource)
{
if (!await _userManager.CheckCaptcha(user, captcha))
{
//是否锁定
if (shouldLockout)
{
//锁定账户
if (await TryLockOutAsync(tenantId, user.Id))
//返回锁定账户
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.LockedOut, tenant, user);
}
//返回无效密码
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.InvalidPassword, tenant, user);
}
//重置登陆失败次数
await UserManager.ResetAccessFailedCountAsync(user);
}
return await CreateLoginByMobileResultAsync(user, tenant);
}
}
创建手机号登陆结果
/// <summary>
/// 创建手机号登陆结果
/// </summary>
/// <param name="user">用户</param>
/// <param name="tenant">租户</param>
/// <returns></returns>
protected virtual async Task<AbpLoginResult<Tenant, User>> CreateLoginByMobileResultAsync(User user,
Tenant tenant = null)
{
//用户未激活
if (!user.IsActive)
//返回用户未激活
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.UserIsNotActive, tenant, user);
//用户手机号未认证
if (user.IsPhoneNumberConfirmed)
//返回用户手机号未认证
return new AbpLoginResult<Tenant, User>(AbpLoginResultType.UserPhoneNumberIsNotConfirmed, tenant, user);
user.LastLoginTime = Clock.Now;
//更新用户
await _userManager.UpdateAsync(user);
await _unitOfWorkManager.Current.SaveChangesAsync();
var principal = await _claimsPrincipalFactory.CreateAsync(user);
return new AbpLoginResult<Tenant, User>(
tenant,
user,
principal.Identity as ClaimsIdentity
);
}
添加webApi
在web.core\Controllers\TokenAuthController.cs
通过手机验证码授权
/// <summary>
/// 通过手机验证码授权
/// </summary>
/// <param name="model">手机验证码Dto</param>
/// <returns></returns>
[HttpPost]
public async Task<AuthenticateResultModel> AuthenticateByPhoneCaptcha([FromBody] AuthenticateByPhoneCaptchaModel model)
{
var loginResult = await GetLoginResultByPhoneCaptchaAsync(
model.PhoneNumber,
model.Captcha,
GetTenancyNameOrNull()
);
var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResult.User.Id
};
}
** 获取登陆结果通过手机验证码**
/// <summary>
/// 获取登陆结果通过手机验证码
/// </summary>
/// <param name="phoneNumber">手机号</param>
/// <param name="captcha">验证码</param>
/// <param name="tenancyName">租户名</param>
/// <returns></returns>
private async Task<AbpLoginResult<Tenant, User>> GetLoginResultByPhoneCaptchaAsync(string phoneNumber, int captcha, string tenancyName)
{
var loginResult = await _logInManager.LoginByMobileAsync(phoneNumber, captcha, tenancyName);
switch (loginResult.Result)
{
case AbpLoginResultType.Success:
return loginResult;
default:
throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, loginResult.User.UserName, tenancyName);
}
}
至此大工告成,还有手机号登录,将在下一篇文章讲解.代码上有详细注解