https://developer.work.weixin.qq.com/document/path/91025
企业微信提供了OAuth的扫码登录授权方式,可以让企业的网站在浏览器内打开时,引导成员使用企业微信扫码登录授权,从而获取成员的身份信息,免去登录的环节。(注:此授权方式需要用户扫码,不同于“网页授权登录”;仅企业内可以使用此种授权方式,第三方服务商不支持使用。)在进行企业微信授权登录之前,需要先在企业的管理端后台创建一个具备“企业微信授权登录”能力的应用。
登录 企业管理端后台->进入需要开启的自建应用->点击 “企业微信授权登录”,进入如下页面
然后点击 "设置授权回调域",输入回调域名,点击“保存”。(域名:需要找运维做解析)
要求配置的授权回调域,必须与访问链接的域名完全一致,如下图:
开发者需要构造如下的链接来获取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
在需要展示企业微信网页登录二维码的网站引入如下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登陆时访问接口或者服务顺序:
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");
http://localhost:53362/connect/token
http://localhost:53362/connect/userinfo
http://localhost:53362/api/abp/application-configuration
返回结果中的auth.grantedPolicies字段,与对应的菜单路由绑定,就可以实现权限的控制。
通过查看IdentityServer4的源码发现,通过GrantType来区分不同的授权方式,除了常规的授权方式之外,在defaut条件中,有自定义授权生成token的方式(ProcessExtensionGrantRequestAsync),可以通过这种方式集成旧的业务系统验证,比如,企业微信扫码、小程序授权、短信登陆、微信登陆、钉钉登陆 等等不同第三方进行集成。
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);
}
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure(builder => {
builder.AddExtensionGrantValidator();
});
}
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
);
http://localhost:53362/connect/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"}
请求地址:
https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
返回结果:
{"UserId":"WangWu","DeviceId":"","errcode":0,"errmsg":"ok"}
获取用户信息(账号、密码)去取token(类似用户账号、密码登录的token)
留言与评论(共有 0 条评论) “” |