为ABP新增手机验证模块

Posted 上将军

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为ABP新增手机验证模块相关的知识,希望对你有一定的参考价值。

当前手机验证基本是标配,但Abp自身并没有实现这个功能,于是有了通过自定义模块实现的想法。

经过研究,发现要实现这个,只要重写和替换包含ReplaceEmailToUsernameOfInputIfNeeds方法的类就可以了。但要实现这个,首先要在IdentityUserManager类中添加FindByPhoneAsync方法用来通过手机号码查询用户。刚开始想通过扩展方法的方式来实现,但发现唯一能用来获取用户集合的公共属性Users并不能用,而又没有其他办法获取存储,于是放弃该方法。现在只能通过自定义IdentityUserManager来实现了。于是在创建了Generic.Abp.PhoneLogin.Domain模块定义了PhoneLoginUserManager对象。

Abp的源代码中搜索ReplaceEmailToUsernameOfInputIfNeeds,会发现有以下5个类包含该方法:

  • Volo.Abp.Account.Web模块的AccountControllerLoginModel
  • Volo.Abp.Account.Web.IdentityServer模块的IdentityServerSupportedLoginModel
  • Volo.Abp.IdentityServer.Domain模块的AbpResourceOwnerPasswordValidator
  • Volo.Abp.OpenIddict.AspNetCore模块的TokenController

Volo.Abp.Account.Web模块的AccountControllerLoginModel

由于Volo.Abp.Account.Web.IdentityServer模块的IdentityServerSupportedLoginModel调用的是Volo.Abp.Account.Web模块的``LoginModelReplaceEmailToUsernameOfInputIfNeeds`方法,因而可以忽悠这个。

对应各个要重写的模块,建立对应的模块就行了。
对于AccountController,通过替换控制器的方式就可实现替换了,具体代码如下:

    [IgnoreAntiforgeryToken]
    [RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)]
    [Controller]
    [ControllerName("Login")]
    [Area("account")]
    [Route("api/account")]
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AccountController), IncludeSelf = true)]
    public class PhoneLoginAccountController : AccountController
    
        public PhoneLoginAccountController(
            SignInManager<IdentityUser> signInManager,
            PhoneLoginUserManager userManager,
            ISettingProvider settingProvider,
            IdentitySecurityLogManager identitySecurityLogManager,
            IOptions<IdentityOptions> identityOptions) :
        base(signInManager, userManager, settingProvider, identitySecurityLogManager, identityOptions)
        
            LocalizationResource = typeof(AccountResource);
            PhoneLoginUserManager = userManager;
        

        protected PhoneLoginUserManager PhoneLoginUserManager  get; 


        protected override async Task ReplaceEmailToUsernameOfInputIfNeeds(UserLoginInfo login)
        

            var userByUsername = await UserManager.FindByNameAsync(login.UserNameOrEmailAddress);
            if (userByUsername != null)
            
                return;
            

            var userByPhone = await PhoneLoginUserManager.FindByPhoneAsync(login.UserNameOrEmailAddress);
            if (userByPhone != null)
            
                login.UserNameOrEmailAddress = userByPhone.UserName;
                return;
            

            if (!ValidationHelper.IsValidEmailAddress(login.UserNameOrEmailAddress))
            
                return;
            

            var userByEmail = await UserManager.FindByEmailAsync(login.UserNameOrEmailAddress);
            if (userByEmail != null)
            
                login.UserNameOrEmailAddress = userByEmail.UserName;
                return;
            


            return;

        


    

ReplaceEmailToUsernameOfInputIfNeeds方法内,主要是通过FindByPhoneAsync方法查找用户,如果找到用户,就用找到的用户名替换登录用户名就行了。

在这里要注意的是使用PhoneLoginUserManager替换原来的IdentityUserManager

对于LoginModel,可以使用只替换LoginModel的方式,这里图方便直接使用了覆盖Login.cshtml的方式。代码就不贴了,大家可以去Github查看源代码。

Volo.Abp.IdentityServer.Domain模块的AbpResourceOwnerPasswordValidator

IdentityServer4是通过IResourceOwnerPasswordValidator接口来实现密码验证的,因而要替换IResourceOwnerPasswordValidator需要点技巧,笔者也是找了一圈才找到的。

具体的ReplaceEmailToUsernameOfInputIfNeeds方法就不贴了,基本上和AccountController没什么不同。

最难部分是在模块定义中替换原有的AbpResourceOwnerPasswordValidator:

    public class GenericAbpPhoneLoginIdentityServerDomainModule : AbpModule
    
        public override void PreConfigureServices(ServiceConfigurationContext context)
        
            PreConfigure<IIdentityServerBuilder>(builder =>
            
                builder.AddResourceOwnerValidator<PhoneLoginResourceOwnerPasswordValidator>();
            );
            context.Services.Replace(
              ServiceDescriptor.Transient<AbpResourceOwnerPasswordValidator, PhoneLoginResourceOwnerPasswordValidator>());
        
    

代码需要先把PhoneLoginResourceOwnerPasswordValidator添加到验证器,然后再替换。

Volo.Abp.OpenIddict.AspNetCore模块的TokenController

这个和替换AccountController没什么不同,就不具体说了。

使用方法

OpenIddict

  • 在应用的Domain.Shared模块引用Generic.Abp.PhoneLogin.Domain.Shared
  • 在应用的Domain模块引用Generic.Abp.PhoneLogin.Domain
  • 在应用的Web模块引用Generic.Abp.PhoneLogin.Account.WebGeneric.Abp.PhoneLogin.OpenIddict.AspNetCore

IdenttityServer

  • 在应用的Domain.Shared模块引用Generic.Abp.PhoneLogin.Domain.Shared
  • 在应用的Domain模块引用Generic.Abp.PhoneLogin.DomainGeneric.Abp.PhoneLogin.IdentityServer.Domain
  • 在应用的Web模块引用Generic.Abp.PhoneLogin.Account.Web

具体示例可查看分支测试identtiyServer4手机登录


源代码:https://github.com/tianxiaode/GenericAbp

以上是关于为ABP新增手机验证模块的主要内容,如果未能解决你的问题,请参考以下文章

为ABP新增手机验证模块之二

ABP Framework V4.4 RC 新增功能介绍

ABP Framework 5.3.0 版本新增功能和变更说明

给 ABP vNext 应用安装私信模块

ABP Framework 5.2 RC 版本发布及新增功能介绍

ABP Framework 7.0 RC 新增功能简介