目中遇到一個(gè)需要微信獲取code支付,其中遇到很多問題,現(xiàn)在做一下總結(jié)。
一、進(jìn)入頁面獲取code:
二、截取code:
?
5網(wǎng)頁實(shí)現(xiàn)微信分享功能
<script type="text/javascript"
src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
/*
* 注意:
* 1. 所有的JS接口只能在公眾號(hào)綁定的域名下調(diào)用,公眾號(hào)開發(fā)者需要先登錄微信公眾平臺(tái)進(jìn)入“公眾號(hào)設(shè)置”的“功能設(shè)置”里填寫“JS接口安全域名”。
* 2. 如果發(fā)現(xiàn)在 Android 不能分享自定義內(nèi)容,請(qǐng)到官網(wǎng)下載最新的包覆蓋安裝,Android 自定義分享接口需升級(jí)至 6.0.2.58 版本及以上。
* 3. 常見問題及完整 JS-SDK 文檔地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
*
* 開發(fā)中遇到問題詳見文檔“附錄5-常見錯(cuò)誤及解決辦法”解決,如仍未能解決可通過以下渠道反饋:
* 郵箱地址:weixin-open@qq.com
* 郵件主題:【微信JS-SDK反饋】具體問題
* 郵件內(nèi)容說明:用簡(jiǎn)明的語言描述問題所在,并交代清楚遇到該問題的場(chǎng)景,可附上截屏圖片,微信團(tuán)隊(duì)會(huì)盡快處理你的反饋。
*/
$(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") { // 請(qǐng)求超時(shí)
} else {
}
},
success : function(data, textStatus) {
if (textStatus == "success") { // 請(qǐng)求成功
//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接口只能在公眾號(hào)綁定的域名下調(diào)用,公眾號(hào)開發(fā)者需要先登錄微信公眾平臺(tái)進(jìn)入“公眾號(hào)設(shè)置”的“功能設(shè)置”里填寫“JS接口安全域名”。
* 2. 如果發(fā)現(xiàn)在 Android 不能分享自定義內(nèi)容,請(qǐng)到官網(wǎng)下載最新的包覆蓋安裝,Android 自定義分享接口需升級(jí)至 6.0.2.58 版本及以上。
* 3. 完整 JS-SDK 文檔地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
*
* 如有問題請(qǐng)通過以下渠道反饋:
* 郵箱地址:weixin-open@qq.com
* 郵件主題:【微信JS-SDK反饋】具體問題
* 郵件內(nèi)容說明:用簡(jiǎn)明的語言描述問題所在,并交代清楚遇到該問題的場(chǎng)景,可附上截屏圖片,微信團(tuán)隊(duì)會(huì)盡快處理你的反饋。
*/
// 發(fā)送給朋友并返回結(jié)果
/* wx.checkJsApi({
jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline'],
success: function(res) {
alert(reg);
}})
// 以鍵值對(duì)的形式返回,可用的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 = "魔法媽咪學(xué)院入學(xué)啦!好媽咪快進(jìn)來!";
var myurl = "http://diy-haier.highset.cn/index.jsp";
//分享給朋友
wx.onMenuShareAppMessage({
title : title, // 分享標(biāo)題
desc : '海爾定制母嬰家電,讓你魔法加身,搞定養(yǎng)娃的大問題!', // 分享描述
link : myurl, // 分享鏈接
imgUrl : 'http://diy-haier.highset.cn/fx.jpg', // 分享圖標(biāo)
type : 'link', // 分享類型,music、video或link,不填默認(rèn)為link
success : function() {
// 用戶確認(rèn)分享后執(zhí)行的回調(diào)函數(shù)
},
cancel : function() {
// 用戶取消分享后執(zhí)行的回調(diào)函數(shù)
}
});
wx.onMenuShareTimeline({
title : title, // 分享標(biāo)題
link : myurl, // 分享鏈接
imgUrl : 'http://diy-haier.highset.cn/fx.jpg', // 分享圖標(biāo)
success : function() {
// 用戶確認(rèn)分享后執(zhí)行的回調(diào)函數(shù)
},
cancel : function() {
// 用戶取消分享后執(zhí)行的回調(diào)函數(shù)
}
});
});
wx.error(function(res) {
alert(res.errMsg);
});
</script>
https://developer.work.weixin.qq.com/document/path/91025
企業(yè)微信提供了OAuth的掃碼登錄授權(quán)方式,可以讓企業(yè)的網(wǎng)站在瀏覽器內(nèi)打開時(shí),引導(dǎo)成員使用企業(yè)微信掃碼登錄授權(quán),從而獲取成員的身份信息,免去登錄的環(huán)節(jié)。(注:此授權(quán)方式需要用戶掃碼,不同于“網(wǎng)頁授權(quán)登錄”;僅企業(yè)內(nèi)可以使用此種授權(quán)方式,第三方服務(wù)商不支持使用。)在進(jìn)行企業(yè)微信授權(quán)登錄之前,需要先在企業(yè)的管理端后臺(tái)創(chuàng)建一個(gè)具備“企業(yè)微信授權(quán)登錄”能力的應(yīng)用。
登錄 企業(yè)管理端后臺(tái)->進(jìn)入需要開啟的自建應(yīng)用->點(diǎn)擊 “企業(yè)微信授權(quán)登錄”,進(jìn)入如下頁面
然后點(diǎn)擊 "設(shè)置授權(quán)回調(diào)域",輸入回調(diào)域名,點(diǎn)擊“保存”。(域名:需要找運(yùn)維做解析)
要求配置的授權(quán)回調(diào)域,必須與訪問鏈接的域名完全一致,如下圖:
開發(fā)者需要構(gòu)造如下的鏈接來獲取code參數(shù):
https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=CORPID&agentid=AGENTID&redirect_uri=REDIRECT_URI&state=STATE
參數(shù)說明:
參數(shù) | 必須 | 說明 |
appid | 是 | 企業(yè)微信的CorpID,在企業(yè)微信管理端查看 |
agentid | 是 | 授權(quán)方的網(wǎng)頁應(yīng)用ID,在具體的網(wǎng)頁應(yīng)用中查看 |
redirect_uri | 是 | 重定向地址,需要進(jìn)行UrlEncode |
state | 否 | 用于保持請(qǐng)求和回調(diào)的狀態(tài),授權(quán)請(qǐng)求后原樣帶回給企業(yè)。該參數(shù)可用于防止csrf攻擊(跨站請(qǐng)求偽造攻擊),建議企業(yè)帶上該參數(shù),可設(shè)置為簡(jiǎn)單的隨機(jī)數(shù)加session進(jìn)行校驗(yàn) |
lang | 否 | 自定義語言,支持zh、en;lang為空則從Headers讀取Accept-Language,默認(rèn)值為zh |
若提示“該鏈接無法訪問”,請(qǐng)檢查參數(shù)是否填寫錯(cuò)誤,如redirect_uri的域名與網(wǎng)頁應(yīng)用的可信域名不一致。
若用戶不在agentid所指應(yīng)用的可見范圍,掃碼時(shí)會(huì)提示無權(quán)限。
返回說明:
用戶允許授權(quán)后,將會(huì)重定向到redirect_uri的網(wǎng)址上,并且?guī)蟘ode和state參數(shù)
redirect_uri?code=CODE&state=STATE
若用戶禁止授權(quán),則重定向后不會(huì)帶上code參數(shù),僅會(huì)帶上state參數(shù)
redirect_uri?state=STATE
示例:
假定當(dāng)前
企業(yè)CorpID:wxCorpId
開啟授權(quán)登錄的應(yīng)用ID:1000000
登錄跳轉(zhuǎn)鏈接:http://api.3dept.com
state設(shè)置為:weblogin@gyoss9
需要配置的授權(quán)回調(diào)域?yàn)椋篴pi.3dept.com
根據(jù)URL規(guī)范,將上述參數(shù)分別進(jìn)行UrlEncode,得到拼接的OAuth2鏈接為:
https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=wxCorpId&agentid=1000000&redirect_uri=回調(diào)域名&state=web_login%40gyoss9
在需要展示企業(yè)微信網(wǎng)頁登錄二維碼的網(wǎng)站引入如下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
步驟二:在需要使用微信登錄的地方實(shí)例JS對(duì)象(React同理)
注意:從wwLogin-1.2.5.js開始需要使用new WwLogin進(jìn)行實(shí)例化
<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="賬號(hào)"
>
<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為實(shí)例對(duì)象,無需登錄時(shí),可手動(dòng)銷毀實(shí)例
}
break;
case 'second':
that.wwLogin = new WwLogin({
'id': 'wx_qrcode', //二維碼顯示區(qū)域div的id值
'appid': '企業(yè)微信后臺(tái)的corpid',
'agentid': '企業(yè)微信后臺(tái)的agentid',
'redirect_uri': '回調(diào)地址(必須為域名模式)', //http://localhost:53362/connect/token
'state': '',
'href': '',
'lang': 'zh',
})
break;
default:break;
}
}
},
此處先介紹一下abpvnext登陸時(shí)訪問接口或者服務(wù)順序:
http://localhost:53362/.well-known/openid-configuration
訪問結(jié)果如下所示:
{
"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
返回結(jié)果中的auth.grantedPolicies字段,與對(duì)應(yīng)的菜單路由綁定,就可以實(shí)現(xiàn)權(quán)限的控制。
通過查看IdentityServer4的源碼發(fā)現(xiàn),通過GrantType來區(qū)分不同的授權(quán)方式,除了常規(guī)的授權(quán)方式之外,在defaut條件中,有自定義授權(quán)生成token的方式(ProcessExtensionGrantRequestAsync),可以通過這種方式集成舊的業(yè)務(wù)系統(tǒng)驗(yàn)證,比如,企業(yè)微信掃碼、小程序授權(quán)、短信登陸、微信登陸、釘釘?shù)顷?等等不同第三方進(jìn)行集成。
public class ExtensionGrantTypes
{
//擴(kuò)展授權(quán)名稱
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可以封裝成接口或服務(wù),參考下面3.1、3.2 部分,方便后期接入
//1、獲取企業(yè)微信訪問令牌access_token
string accessToken = "123123123123";
//2、獲取企業(yè)微信訪問用戶身份(企業(yè)微信號(hào)) UserId
string userId = "ZhangSan";
//3、根據(jù)企業(yè)微信用戶身份userId找到業(yè)務(wù)庫用戶表對(duì)比,找到真實(shí)的用戶信息
if (!string.IsNullOrEmpty(userId))
{
context.Result = await ServerValidate("", ""); //可以把UserId傳進(jìn)去
}
else
context.Result = new GrantValidationResult(IdentityServer4.Models.TokenRequestErrors.InvalidGrant);
}
/// <summary>
/// 服務(wù)器端驗(yàn)證并輸出用戶信息,后續(xù)自動(dòng)生成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); //業(yè)務(wù)庫用戶
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
是不是發(fā)現(xiàn)這個(gè)鏈接熟悉,沒錯(cuò)就是上面“@@登陸順序”部分,前端按之前賬號(hào)、密碼登陸方式調(diào)用即可,切換為下面的參數(shù),后續(xù)同@@登陸順序部分一致。
請(qǐng)求地址:
https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
#corpid、corpsecret換為自己的corpid、應(yīng)用secret
返回結(jié)果:
{"access_token":"sdfadsf","expires_in":15,"errcode":0,"errmsg":"ok"}
請(qǐng)求地址:
https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
返回結(jié)果:
{"UserId":"WangWu","DeviceId":"","errcode":0,"errmsg":"ok"}
獲取用戶信息(賬號(hào)、密碼)去取token(類似用戶賬號(hào)、密碼登錄的token)
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。