• Abp.Zero 手机号免密登录验证与号码绑定功能的实现(二):改造Abp默认实现



    接下来我们重写原Abp的部分实现,来驳接手机号相关业务。

    改造User类

    重写PhoneNumber使得电话号码为必填项,和中国大陆手机号11位长度

    public new const int MaxPhoneNumberLength = 11;
    
    [Required]
    [StringLength(MaxPhoneNumberLength)]
    public override string PhoneNumber { get; set; }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    改造UserStore类

    扩展通过PhoneNumber查找用户的方法

    
    public async Task FindByNameOrPhoneNumberAsync(string userNameOrPhoneNumber)
    {
    
        return await UserRepository.FirstOrDefaultAsync(
            user => user.NormalizedUserName == userNameOrPhoneNumber || user.PhoneNumber == userNameOrPhoneNumber
        );
    }
    
    [UnitOfWork]
    public async Task FindByNameOrPhoneNumberAsync(int? tenantId, string userNameOrPhoneNumber)
    {
        using (_unitOfWorkManager.Current.SetTenantId(tenantId))
        {
            return await FindByNameOrPhoneNumberAsync(userNameOrPhoneNumber);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    改造UserManager类

    添加检测重复电话号码的方法CheckDuplicateUsernameOrPhoneNumber

    
    public async Task CheckDuplicateUsernameOrPhoneNumber(long? expectedUserId, string userName, string phone)
    {
        var user = await FindByNameAsync(userName);
        if (user != null && user.Id != expectedUserId)
        {
            throw new UserFriendlyException(string.Format(L("Identity.DuplicateUserName"), userName));
        }
    
        user = await FindByNameOrPhoneNumberAsync(GetCurrentTenantId(), phone);
        if (user != null && user.Id != expectedUserId)
        {
            throw new UserFriendlyException("电话号码重复", phone);
        }
    
        return IdentityResult.Success;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    重写对用户的Create和Update,使其先检测是否重复电话号码。

    //override
    
    public override async Task CreateAsync(User user)
    {
        var result = await CheckDuplicateUsernameOrPhoneNumber(user.Id, user.UserName, user.PhoneNumber);
        if (!result.Succeeded)
        {
            return result;
        }
    
    
        return await base.CreateAsync(user);
    }
    
    public override async Task UpdateAsync(User user)
    {
        var result = await CheckDuplicateUsernameOrPhoneNumber(user.Id, user.UserName, user.PhoneNumber);
        if (!result.Succeeded)
        {
            return result;
        }
    
        return await base.UpdateAsync(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    改造LogInManager类

    分别重写LoginAsyncInternal,TryLoginFromExternalAuthenticationSourcesAsync两个方法,在用Email找不到用户之后,添加用手机号码查找用户的逻辑,添加的代码如下:

    ...
    if (user == null)
    {
        user = await userManager.FindByNameOrPhoneNumberAsync(tenantId, combinationName);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    编写验证源

    新建电话号码验证源类PhoneNumberExternalAuthenticationSource,并实现验证码校验逻辑,具体的代码

    public class PhoneNumberExternalAuthenticationSource : DefaultExternalAuthenticationSource, ITransientDependency
    {
        private readonly CaptchaManager captchaManager;
    
        public PhoneNumberExternalAuthenticationSource(CaptchaManager captchaManager)
        {
            this.captchaManager=captchaManager;
        }
        /// 
        public override string Name { get; } = "SMS验证码登录";
    
        /// 
        public override async Task TryAuthenticateAsync(string phoneNumber, string token, Tenant tenant)
        {
            //for test
            //return true;
            var currentItem = await captchaManager.GetToken(token);
            if (currentItem==null || currentItem.PhoneNumber!=phoneNumber || currentItem.Purpose!=CaptchaPurpose.LOGIN)
            {
                return false;
            }
            await captchaManager.RemoveToken(token);
            return true;
        }
    
        /// 
        public override Task CreateUserAsync(string userNameOrEmailAddress, Tenant tenant)
        {
            var seed = Guid.NewGuid().ToString("N").Substring(0, 7);
            var surname = "手";
            var name = "机用户"+seed;
            var userName = PinyinUtil.PinYin(surname+name);
    
                var result = new User()
                {
                    Surname = surname,
                    Name = name,
                    UserName =  userName,
                    IsPhoneNumberConfirmed = true,
                    IsActive=true,
                    TenantId = tenant?.Id,
                    PhoneNumber = userNameOrEmailAddress,
                    Settings = null,
                    IsEmailConfirmed = true,
                    EmailAddress=$"{userName}@abc.com"
                };
            return Task.FromResult(result);
    
        }
    
        /// 
        public override Task UpdateUserAsync(User user, Tenant tenant)
        {
            return Task.FromResult(0);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    配置

    在Web.Core项目中的WebCoreModule文件中,将PhoneNumberExternalAuthenticationSource添加至扩展身份验证源配置中

    private void ConfigureExternalAuth()
    {
        var userManagementConfig = IocManager.Resolve();
        userManagementConfig.ExternalAuthenticationSources.Add(typeof(PhoneNumberExternalAuthenticationSource));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在Web.Host项目中的 appsettings.json 文件中,添加AliyunSms库的相关配置,详细说明请参考AbpBoilerplate.Sms

      "AliyunSms": {
        "RegionId": "cn-hangzhou",
        "AccessKey": "{Your AccessKey}",  //阿里云后台管理页面中获取AccessKey
        "AccessKeySecret": "{Your AccessKeySecret}"  //阿里云后台管理页面中获取AccessKeySecret
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5

    至此,后端的所有任务结束,下一章将介绍前端项目的搭建

    项目地址

    Github:matoapp-samples

  • 相关阅读:
    Vue前端整合Element Ui
    Etcd 概要 机制 和使用场景
    Linux系统下的Swift与Ceph分布式存储解决方案
    程序员进阶之路,该怎么走?
    VSCODE中使用Django处理后端data和data models
    我的创作纪念日
    web前端学习笔记三
    840. 矩阵中的幻方。python三连双等 a==b==c
    【MyBatis笔记02】MyBatis核心配置文件介绍
    AM@邻域@极限定义中的符号说明
  • 原文地址:https://blog.csdn.net/jevonsflash/article/details/127576427