ABP中module-zero快速集成微信用户认证

前言

当我们刚开始尝试ABP开发框架的时候,我们一般会下载它的项目模板,启动起来试试。

当我们选择集成了module-zero(勾选包含用户登陆注册功能时)的单页应用项目模板的时候——这里有两个方法获取启动模板:

  1. aspnetboilerplate.com下载;
  2. 如果官网打开比较慢,也可以使用命令行工具abplus init在本地执行从github上拉取项目模板。具体使用方法参考前一篇ABP中的AsyncCrudAppService介绍或者Github.com/personball/Abp-CLI

以vue单页应用为例。

安装dotnet core 全局工具ABPTools

 dotnet tool install -g AbpTools

初始化项目:

abplus init -t vue

默认项目名AbpDemo

打开解决方案aspnet-core/AbpDemo.sln,按以下路径找到目标代码:

AbpDemo.Web.Core项目>Authentication文件夹>External文件夹

在这个目录里,ABP项目模板放置了ExternalAuthenticate机制相关的所有代码。

而启动项目后,Swagger界面上TokenAuth/ExternalAuthenticate接口则是在同一个项目里的Controllers文件夹下的TokenAuthController

首先分析一下External文件夹里的代码的作用

External文件夹下有以下几个代码文件:

  • ExternalAuthConfiguration是一个配置类,我们自己拓展的Provider要注册到这里;
  • ExternalAuthManager统一对外提供服务;
  • ExternalAuthProviderApiBase一个Provider基类,方便扩展自己的ExternalAuthenticate;
  • ExternalAuthUserInfo封装用户的认证信息;
  • ExternalLoginProviderInfo封装Provider要用到的认证信息,按Wechat来说,这里封装AppId和AppSecret;
  • IExternalAuthConfiguration配置类的接口;
  • IExternalAuthManager接口;
  • IExternalAuthProviderApi接口;

除去后面的三个接口,我们集成微信用户认证只要用到ExternalAuthProviderApiBaseExternalAuthUserInfo

只要定义一个自己的Provider并注册到ExternalAuthConfiguration就可以重用ExternalAuthenticate接口统一的流程。

开始集成微信用户认证

  1. 我们在AbpDemo.Web.Core项目>Authentication文件夹>External文件夹下新增Wechat文件夹;
  2. Wechat文件夹下新建WechatAuthProviderApi类(该类代码随后附上);
  3. Wechat文件夹下新建WechatAuthUserInfo类用于对应微信返回的用户信息(该类代码随后附上);
  4. AbpDemoWebCoreModule中注册及配置(AbpDemo.Web.Host的appsettings.json要新增配置,下附);

只要上面四个步骤,就可以完成微信认证集成(调用微信认证接口,拿code换sessionKey和openID的代码不再赘述)。

下面是提到的各部分代码:

WechatAuthProviderApi继承ExternalAuthProviderApiBase

public class WechatAuthProviderApi : ExternalAuthProviderApiBase
{
    public ILogger Logger { get; set; }

    public WechatAuthProviderApi()
    {
        Logger = NullLogger.Instance;
    }

    public override async Task<ExternalAuthUserInfo> GetUserInfo(string accessCode)
    {
        var client = HttpApiClient.Create<IWeChatApi>();

        var authResult = await client.AuthCodeAsync(ProviderInfo.ClientId, ProviderInfo.ClientSecret, accessCode);

        if (authResult.errcode == 0)
        {
            return new WechatAuthUserInfo
            {
                EmailAddress = $"{authResult.openid}@AbpDemo.com",
                Name = $"{authResult.openid}",
                Provider = ProviderInfo.Name,
                ProviderKey = authResult.openid,
                Surname = authResult.openid,
                SessionKey = authResult.session_key,
                OpenId = authResult.openid
            };
        }
        else
        {
            Logger.Error($"{GetType().FullName}:{authResult.errcode},{authResult.errmsg}");
            throw new AbpProjectNameBusinessException(ErrorCode.WechatAuthByCodeFailed);
        }
    }
}

WechatAuthUserInfo定义:

public class WechatAuthUserInfo : ExternalAuthUserInfo
{
    public string SessionKey { get; set; }
    
    public string OpenId { get; set; }
}

AbpDemoWebCoreModule中注册:

private void ConfigureTokenAuth()
{
    ...原有代码略

    //wechat login config
    if (_appConfiguration["Authentication:Wechat:IsEnabled"].ToLower() == bool.TrueString.ToLower())
    {
        IocManager.Register<IExternalAuthConfiguration, ExternalAuthConfiguration>();
        var externalAuthConfig = IocManager.Resolve<IExternalAuthConfiguration>();
        externalAuthConfig.Providers.Add(new ExternalLoginProviderInfo(
            "Wechat",
            _appConfiguration["Authentication:Wechat:AppId"],
            _appConfiguration["Authentication:Wechat:AppSecret"],
            typeof(WechatAuthProviderApi)));
    }
}

其中Wechat这个ProviderName要和调用TokenAuth/ExternalAuthenticate接口时传递的值保持一致。

appsettings.json 中新增相应配置:

"Authentication": {
    "JwtBearer": {
        ...原有JwtBearer配置略
    },
    "Wechat": {
    "IsEnabled": "false",//启用时改成true
    "AppId": "",
    "AppSecret": ""
    }
}

这样,就实现了微信用户认证机制的集成。

基本完成,还有一点点

首先,上面展示的代码var client = HttpApiClient.Create<IWeChatApi>();没有包含IWeChatApi的实现(用了WebApiClient.JIT包)以及接口返回结果的定义。

然后,我们集成微信用户认证的话,一般也需要同步用户的微信信息,会需要保存和更新sessionKey等。
这就需要对TokenAuth/ExternalAuthenticate的流程稍微做一些处理。

最后,在真正调用TokenAuth/ExternalAuthenticate的时候:

  • authProvider :填”Wechat”
  • providerKey :对应微信openId
  • providerAccessCode”: 对应微信code

这里如果providerKey不想填(比如我们想记录unionId,不想记录openId作为唯一标识,unionId一开始前端是拿不到明文的)。
可以去掉传入Dto中的Required,然后去掉TokenAuthController.GetExternalUserInfo方法中对ProviderKey的验证逻辑。

综上,完整参考代码可以通过以下命令拉取:

abplus init Abplus.ZeroDemo -T personball/abplus-zero-template -t vue

今天就到这里,谢谢阅读,新年快乐。

Published: January 01 2019

prize