目中遇到一个需要微信获取code支付,其中遇到很多问题,现在做一下总结。
一、进入页面获取code:
二、截取code:
5网页实现微信分享功能
<script type="text/javascript"
src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
/*
* 注意:
* 1. 所有的JS接口只能在公众号绑定的域名下调用,公众号开发者需要先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
* 2. 如果发现在 Android 不能分享自定义内容,请到官网下载最新的包覆盖安装,Android 自定义分享接口需升级至 6.0.2.58 版本及以上。
* 3. 常见问题及完整 JS-SDK 文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
*
* 开发中遇到问题详见文档“附录5-常见错误及解决办法”解决,如仍未能解决可通过以下渠道反馈:
* 邮箱地址:weixin-open@qq.com
* 邮件主题:【微信JS-SDK反馈】具体问题
* 邮件内容说明:用简明的语言描述问题所在,并交代清楚遇到该问题的场景,可附上截屏图片,微信团队会尽快处理你的反馈。
*/
$(function() {
var url = location.href.split('#')[0];
//alert(url);
$.ajax({
url : "wap_sign_signature.htm",
data : {
"url" : url
},
dataType : "json",
timeout : 5000,
error : function(XMLHttpRequest, textStatus, errorThrown) {
if (textStatus == "timeout") { // 请求超时
} else {
}
},
success : function(data, textStatus) {
if (textStatus == "success") { // 请求成功
//alert(data.nonceStr);
//alert(data.timestamp);
//alert(data.signature);
wx.config({
debug : false,
appId : 'wxc4cf8e62667f92ea',
timestamp : data.timestamp,
nonceStr : data.nonceStr,
signature : data.signature,
jsApiList : [ 'onMenuShareAppMessage',
'onMenuShareTimeline' ]
});
}
}
});
});
</script>
<script type="text/javascript">
/*
* 注意:
* 1. 所有的JS接口只能在公众号绑定的域名下调用,公众号开发者需要先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
* 2. 如果发现在 Android 不能分享自定义内容,请到官网下载最新的包覆盖安装,Android 自定义分享接口需升级至 6.0.2.58 版本及以上。
* 3. 完整 JS-SDK 文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
*
* 如有问题请通过以下渠道反馈:
* 邮箱地址:weixin-open@qq.com
* 邮件主题:【微信JS-SDK反馈】具体问题
* 邮件内容说明:用简明的语言描述问题所在,并交代清楚遇到该问题的场景,可附上截屏图片,微信团队会尽快处理你的反馈。
*/
// 发送给朋友并返回结果
/* wx.checkJsApi({
jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline'],
success: function(res) {
alert(reg);
}})
// 以键值对的形式返回,可用的api值true,不可用为false// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}});
}) */
wx.ready(function() {
/* wx.checkJsApi({
jsApiList: ['onMenuShareAppMessage'],
success:function(res){
alert(res.checkResult.chooseImage);
}
}) ; */
var title = "魔法妈咪学院入学啦!好妈咪快进来!";
var myurl = "http://diy-haier.highset.cn/index.jsp";
//分享给朋友
wx.onMenuShareAppMessage({
title : title, // 分享标题
desc : '海尔定制母婴家电,让你魔法加身,搞定养娃的大问题!', // 分享描述
link : myurl, // 分享链接
imgUrl : 'http://diy-haier.highset.cn/fx.jpg', // 分享图标
type : 'link', // 分享类型,music、video或link,不填默认为link
success : function() {
// 用户确认分享后执行的回调函数
},
cancel : function() {
// 用户取消分享后执行的回调函数
}
});
wx.onMenuShareTimeline({
title : title, // 分享标题
link : myurl, // 分享链接
imgUrl : 'http://diy-haier.highset.cn/fx.jpg', // 分享图标
success : function() {
// 用户确认分享后执行的回调函数
},
cancel : function() {
// 用户取消分享后执行的回调函数
}
});
});
wx.error(function(res) {
alert(res.errMsg);
});
</script>
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文件中)
<script src="https://rescdn.qqmail.com/node/ww/wwopenmng/js/sso/wwLogin-1.0.0.js" type="text/javascript"></script>
版本:
旧版: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进行实例化
<template>
<el-tabs v-model="activeName" @tab-click="handleClick" >
<el-tab-pane label="账户密码登录" name="first" class="wechart-pane">
<el-form-item prop="tenant">
<el-input
v-model="loginForm.tenant"
type="text"
auto-complete="off"
placeholder="租户"
>
<i
slot="prefix"
class="el-input__icon el-icon-office-building"
></i>
</el-input>
</el-form-item>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
icon="el-icon-user"
type="text"
auto-complete="off"
placeholder="账号"
>
<i slot="prefix" class="el-input__icon el-icon-user"></i>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
icon="el-icon-unlock"
type="password"
auto-complete="off"
placeholder="密码"
@keyup.enter.native="handleLogin"
>
<i slot="prefix" class="el-input__icon el-icon-unlock"></i>
</el-input>
</el-form-item>
<el-form-item>
<el-button
:loading="loading"
size="medium"
type="primary"
style="width: 100%"
@click.native.prevent="handleLogin"
>
登 录
</el-button>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="扫码登录" name="second" class="wechart-pane" >
<div id="wx_qrcode"></div>
</el-tab-pane>
</el-tabs>
</template>
脚本部分:定义全局变量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<Volo.Abp.Identity.IdentityUser> _userManager;
private readonly IJsonSerializer _jsonSerializer;
public WeChatQrCodeGrantValidator(
UserManager<Volo.Abp.Identity.IdentityUser> 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);
}
/// <summary>
/// 服务器端验证并输出用户信息,后续自动生成token
/// </summary>
/// <param name="loginProvider"></param>
/// <param name="providerKey"></param>
/// <returns></returns>
private async Task<GrantValidationResult> 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> claimsIdentity = new List<ClaimsIdentity>();
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<IIdentityServerBuilder>(builder => {
builder.AddExtensionGrantValidator<WeChatQrCodeGrantValidator>();
});
}
await CreateClientAsync(
name: "wechat-web",
scopes: commonScopes.Union(new[] {
"IdentityService", "InternalGateway", "WebAppGateway", "BusinessService","WeChat"
}),
grantTypes: new[] { "WeChat" },
//redirectUri: $"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)
*请认真填写需求信息,我们会在24小时内与您取得联系。