ASP.NET Core 学习记录 - AbpVnext 扩展企业微信扫码登录-Vue框架

1、开始开发

https://developer.work.weixin.qq.com/document/path/91025

企业微信提供了OAuth的扫码登录授权方式,可以让企业的网站在浏览器内打开时,引导成员使用企业微信扫码登录授权,从而获取成员的身份信息,免去登录的环节。(注:此授权方式需要用户扫码,不同于“网页授权登录”;仅企业内可以使用此种授权方式,第三方服务商不支持使用。)在进行企业微信授权登录之前,需要先在企业的管理端后台创建一个具备“企业微信授权登录”能力的应用。

1.1 企业微信扫码登陆接入流程


1.2 开启网页授权登陆

登录 企业管理端后台->进入需要开启的自建应用->点击 “企业微信授权登录”,进入如下页面



然后点击 "设置授权回调域",输入回调域名,点击“保存”。(域名:需要找运维做解析)

要求配置的授权回调域,必须与访问链接的域名完全一致,如下图:


1.3 构造独立窗口登陆二维码

开发者需要构造如下的链接来获取code参数:

https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=CORPID&agentid=AGENTID&redirect_uri=REDIRECT_URI&state=STATE

参数说明:

参数

必须

说明

appid

企业微信的CorpID,在企业微信管理端查看

agentid

授权方的网页应用ID,在具体的网页应用中查看

redirect_uri

重定向地址,需要进行UrlEncode

state

用于保持请求和回调的状态,授权请求后原样带回给企业。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议企业带上该参数,可设置为简单的随机数加session进行校验

lang

自定义语言,支持zh、en;lang为空则从Headers读取Accept-Language,默认值为zh

若提示“该链接无法访问”,请检查参数是否填写错误,如redirect_uri的域名与网页应用的可信域名不一致。

若用户不在agentid所指应用的可见范围,扫码时会提示无权限。

返回说明:

用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数

redirect_uri?code=CODE&state=STATE

若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数

redirect_uri?state=STATE

示例:

假定当前
企业CorpID:wxCorpId
开启授权登录的应用ID:1000000
登录跳转链接:http://api.3dept.com
state设置为:weblogin@gyoss9
需要配置的授权回调域为:api.3dept.com
根据URL规范,将上述参数分别进行UrlEncode,得到拼接的OAuth2链接为:
https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=wxCorpId&agentid=1000000&redirect_uri=回调域名&state=web_login%40gyoss9

1.4 构造内嵌登陆二维码

在需要展示企业微信网页登录二维码的网站引入如下JS文件(支持https):

步骤一:引入JS文件 (vue框架的话,放在index.html文件中)

版本:

旧版:http://rescdn.qqmail.com/node/ww/wwopenmng/js/sso/wwLogin-1.0.0.js
新版(20220415更新):http://wwcdn.weixin.qq.com/node/wework/wwopen/js/wwLogin-1.2.7.js

步骤二:在需要使用微信登录的地方实例JS对象(React同理)

注意:从wwLogin-1.2.5.js开始需要使用new WwLogin进行实例化

脚本部分:定义全局变量wwLogin,方便后面销毁

handleClick(tab, event) {
  const that = this;
  if (tab){
    switch (tab.name) {
      case 'first':
        if (that.wwLogin != null){
          that.wwLogin.destroyed(); // 注意wwLogin为实例对象,无需登录时,可手动销毁实例
        }
        break;
      case 'second':
        that.wwLogin = new WwLogin({
          'id': 'wx_qrcode', //二维码显示区域div的id值
          'appid': '企业微信后台的corpid',
          'agentid': '企业微信后台的agentid',
          'redirect_uri': '回调地址(必须为域名模式)', //http://localhost:53362/connect/token
          'state': '',
          'href': '',
          'lang': 'zh',
        })
        break;
      default:break;
    }
  }
},

@@登陆顺序:

此处先介绍一下abpvnext登陆时访问接口或者服务顺序:

1. 发现文档配置

http://localhost:53362/.well-known/openid-configuration

访问结果如下所示:

{
    "issuer": "http://localhost:53362",
    "jwks_uri": "http://localhost:53362/.well-known/openid-configuration/jwks",
    "authorization_endpoint": "http://localhost:53362/connect/authorize",
    "token_endpoint": "http://localhost:53362/connect/token",
    "userinfo_endpoint": "http://localhost:53362/connect/userinfo",
    "end_session_endpoint": "http://localhost:53362/connect/endsession",
    "check_session_iframe": "http://localhost:53362/connect/checksession",
    "revocation_endpoint": "http://localhost:53362/connect/revocation",
    "introspection_endpoint": "http://localhost:53362/connect/introspect",
    "device_authorization_endpoint": "http://localhost:53362/connect/deviceauthorization",
    "frontchannel_logout_supported": true,
    "frontchannel_logout_session_supported": true,
    "backchannel_logout_supported": true,
    "backchannel_logout_session_supported": true,
    "scopes_supported": [
        "openid",
        "profile",
        "email",
        "address",
        "phone",
        "role",
        "BaseService",
        "InternalGateway",
        "WebAppGateway",
        "BusinessService",
        "offline_access"
    ],
    "claims_supported": [
        "sub",
        "birthdate",
        "family_name",
        "gender",
        "given_name",
        "locale",
        "middle_name",
        "name",
        "nickname",
        "picture",
        "preferred_username",
        "profile",
        "updated_at",
        "website",
        "zoneinfo",
        "email",
        "email_verified",
        "address",
        "phone_number",
        "phone_number_verified",
        "role"
    ],
    "grant_types_supported": [
        "authorization_code",
        "client_credentials",
        "refresh_token",
        "implicit",
        "password",
        "urn:ietf:params:oauth:grant-type:device_code"
    ],
    "response_types_supported": [
        "code",
        "token",
        "id_token",
        "id_token token",
        "code id_token",
        "code token",
        "code id_token token"
    ],
    "response_modes_supported": [
        "form_post",
        "query",
        "fragment"
    ],
    "token_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post"
    ],
    "id_token_signing_alg_values_supported": [
        "RS256"
    ],
    "subject_types_supported": [
        "public"
    ],
    "code_challenge_methods_supported": [
        "plain",
        "S256"
    ],
    "request_parameter_supported": true
}

代码方式获取(Url可配置在appsettings.json或者nacos配置中心):

var client = new HttpClient() ; 
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:53362/.well-known/openid-configuration"); 

2. 获取token的Url地址

http://localhost:53362/connect/token

3. 根据token获取用户信息地址

http://localhost:53362/connect/userinfo

4. vue-element-admin菜单权限是使用用户角色来控制的,我们不需要role,通过接口:

http://localhost:53362/api/abp/application-configuration

返回结果中的auth.grantedPolicies字段,与对应的菜单路由绑定,就可以实现权限的控制。

2、企业微信扫码成功后回调/connect/token原理:

通过查看IdentityServer4的源码发现,通过GrantType来区分不同的授权方式,除了常规的授权方式之外,在defaut条件中,有自定义授权生成token的方式(ProcessExtensionGrantRequestAsync),可以通过这种方式集成旧的业务系统验证,比如,企业微信扫码、小程序授权、短信登陆、微信登陆、钉钉登陆 等等不同第三方进行集成。

2.1自定义授权实现

public class ExtensionGrantTypes
    {
        //扩展授权名称
        public const string WeChatQrCodeGrantType = "WeChat";
    }

    public class WeChatQrCodeGrantValidator : IExtensionGrantValidator
    {
        public string GrantType => ExtensionGrantTypes.WeChatQrCodeGrantType;
        private readonly DateTime DateTime1970 = new DateTime(1970, 1, 1).ToLocalTime();
        private readonly UserManager _userManager;
        private readonly IJsonSerializer _jsonSerializer;

        public WeChatQrCodeGrantValidator(
UserManager userLoginManager,
IJsonSerializer jsonSerializer)
        {
            _userManager = userLoginManager;
            _jsonSerializer = jsonSerializer;
        }
        public async Task ValidateAsync(ExtensionGrantValidationContext context)
        {

            string code = context.Request.Raw.Get("Code");
            if (string.IsNullOrEmpty(code))
            {
                context.Result = new GrantValidationResult(IdentityServer4.Models.TokenRequestErrors.InvalidGrant);
            }

            //下面第1、2可以封装成接口或服务,参考下面3.1、3.2 部分,方便后期接入

            //1、获取企业微信访问令牌access_token
            string accessToken = "123123123123";

            //2、获取企业微信访问用户身份(企业微信号) UserId
            string userId = "ZhangSan";

            //3、根据企业微信用户身份userId找到业务库用户表对比,找到真实的用户信息

            if (!string.IsNullOrEmpty(userId))
            {
                context.Result = await ServerValidate("", ""); //可以把UserId传进去
            }
            else
                context.Result = new GrantValidationResult(IdentityServer4.Models.TokenRequestErrors.InvalidGrant);

        }
                

        /// 
        /// 服务器端验证并输出用户信息,后续自动生成token
        /// 
        /// 
        /// 
        /// 
        private async Task ServerValidate(string loginProvider, string providerKey)
        {
            var user = await _userManager.FindByLoginAsync(loginProvider, providerKey); //业务库用户
            if (user == null)
                return new GrantValidationResult(IdentityServer4.Models.TokenRequestErrors.InvalidGrant);
            var principal = new ClaimsPrincipal();
            List claimsIdentity = new List();
            ClaimsIdentity identity = new ClaimsIdentity();
            identity.AddClaim(new Claim("sub", user.Id.ToString()));
            identity.AddClaim(new Claim("tenantid", user.TenantId.ToString())); //租户Id
            identity.AddClaim(new Claim("idp", "local"));
            identity.AddClaim(new Claim("amr", loginProvider));
            long authTime = (long)(DateTime.Now.ToLocalTime() - DateTime1970).TotalSeconds;
            identity.AddClaim(new Claim("auth_time", authTime.ToString()));
            claimsIdentity.Add(identity);
            principal.AddIdentities(claimsIdentity);
            return new GrantValidationResult(principal);
        }
    }

2.2 添加扩展方法(在实现AbpModel类中)

public override void PreConfigureServices(ServiceConfigurationContext context)
{
    context.Services.PreConfigure(builder => { 
        builder.AddExtensionGrantValidator(); 
    });
}

2.3 在Domain项目中的identityServer 文件夹中的种子数据添加grantTypes(CreateClientAsync()下)

await CreateClientAsync(
    name: "wechat-web",
    scopes: commonScopes.Union(new[] {
         "IdentityService", "InternalGateway", "WebAppGateway", "BusinessService","WeChat"
    }),
    grantTypes: new[] { "WeChat" },
    //redirectUri: #34;http://localhost:44307/authentication/login-callback",
    requireClientSecret: false
);

2.4 前三步执行后,无需执行Add-Migration/Update-Database命令,直接启动服务,种子数据会自动入库并配置好。

2.5 访问token

http://localhost:53362/connect/token

是不是发现这个链接熟悉,没错就是上面“@@登陆顺序”部分,前端按之前账号、密码登陆方式调用即可,切换为下面的参数,后续同@@登陆顺序部分一致。


3、企业微信获取token和用户账号

3.1 获取访问令牌access_token

请求地址:

https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
#corpid、corpsecret换为自己的corpid、应用secret

返回结果:

{"access_token":"sdfadsf","expires_in":15,"errcode":0,"errmsg":"ok"}

3.2 获取访问用户身份(企业微信号)

请求地址:

https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE

返回结果:

{"UserId":"WangWu","DeviceId":"","errcode":0,"errmsg":"ok"}

3.3获取UserId与本地库User表比对,找到真实的用户信息

获取用户信息(账号、密码)去取token(类似用户账号、密码登录的token)


(此处已添加书籍卡片,请到今日头条客户端查看)(此处已添加纪录片卡片,请到今日头条客户端查看)
ASP   NET   Core
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章