Abp Core adds SMS verification code login (dynamic password login)

Exchange QQ number: 555913397
If you have any questions, you can join the group to communicate with
Abp Core Add SMS verification code to log in (dynamic password login)

At present, Chinese websites have rarely used e-mails. Basically, mobile phone numbers are used as accounts. Sometimes careless users will forget their passwords. The website provides a more convenient SMS verification code to log in. Use the SMS verification code to replace the password. , Ben Xiaobai also wanted to implement this function, but Abp did not provide such a method, so after unremitting efforts, I finally got it, no more nonsense to say the following code.
1. Add the method of querying users by mobile phone number
in 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. Add the verification code verification method
in 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. Add mobile verification code login function
in Core\Authorization\LoginManager.cs

Mobile verification code login

    /// <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;
    }

Mobile phone verification code login internal method

    /// <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);
        }
    }

Create mobile number login result

    /// <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
        );
    }

Add webApi to authorize through mobile phone verification code
in 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
        };
    }

** Get the login result through the mobile phone verification code**

    /// <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);
        }
    }

At this point, the work is completed, and there is still a mobile phone number to log in, which will be explained in the next article. There are detailed comments on the code. If you don't understand anything, you can join the QQ group to communicate.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325771895&siteId=291194637