jssdk验签
This commit is contained in:
parent
e1dd80c3ee
commit
89498e443e
|
|
@ -81,5 +81,15 @@ export declare class WechatPublicInstance {
|
|||
count: number;
|
||||
noContent?: 0 | 1;
|
||||
}): Promise<any>;
|
||||
getTicket(): Promise<string>;
|
||||
private randomString;
|
||||
signatureJsSDK(options: {
|
||||
url: string;
|
||||
}): Promise<{
|
||||
signature: any;
|
||||
noncestr: string;
|
||||
timestamp: number;
|
||||
appId: string;
|
||||
}>;
|
||||
}
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ var tslib_1 = require("tslib");
|
|||
require('../../fetch');
|
||||
var crypto_1 = tslib_1.__importDefault(require("crypto"));
|
||||
var buffer_1 = require("buffer");
|
||||
var sha1 = require('sha1');
|
||||
var WechatPublicInstance = /** @class */ (function () {
|
||||
function WechatPublicInstance(appId, appSecret, accessToken, externalRefreshFn) {
|
||||
this.appId = appId;
|
||||
|
|
@ -88,7 +89,15 @@ var WechatPublicInstance = /** @class */ (function () {
|
|||
var result, _a, access_token, openid, unionid, scope, refresh_token, is_snapshotuser, expires_in;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, this.access("https://api.weixin.qq.com/sns/oauth2/access_token?appid=".concat(this.appId, "&secret=").concat(this.appSecret, "&code=").concat(code, "&grant_type=authorization_code"), { access_token: 'aaa', openid: code, unionid: code, refresh_token: 'aaa', is_snapshotuser: false, expires_in: 30, scope: 'userinfo' })];
|
||||
case 0: return [4 /*yield*/, this.access("https://api.weixin.qq.com/sns/oauth2/access_token?appid=".concat(this.appId, "&secret=").concat(this.appSecret, "&code=").concat(code, "&grant_type=authorization_code"), {
|
||||
access_token: 'aaa',
|
||||
openid: code,
|
||||
unionid: code,
|
||||
refresh_token: 'aaa',
|
||||
is_snapshotuser: false,
|
||||
expires_in: 30,
|
||||
scope: 'userinfo',
|
||||
})];
|
||||
case 1:
|
||||
result = _b.sent();
|
||||
_a = typeof result === 'string' ? JSON.parse(result) : result, access_token = _a.access_token, openid = _a.openid, unionid = _a.unionid, scope = _a.scope, refresh_token = _a.refresh_token, is_snapshotuser = _a.is_snapshotuser, expires_in = _a.expires_in;
|
||||
|
|
@ -111,7 +120,12 @@ var WechatPublicInstance = /** @class */ (function () {
|
|||
var result, access_token, refresh_token, expires_in, scope;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, this.access("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=".concat(this.appId, "&grant_type=refresh_token&refresh_token=").concat(refreshToken), { access_token: 'aaa', refresh_token: 'aaa', expires_in: 30, scope: 'userinfo' })];
|
||||
case 0: return [4 /*yield*/, this.access("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=".concat(this.appId, "&grant_type=refresh_token&refresh_token=").concat(refreshToken), {
|
||||
access_token: 'aaa',
|
||||
refresh_token: 'aaa',
|
||||
expires_in: 30,
|
||||
scope: 'userinfo',
|
||||
})];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
access_token = result.access_token, refresh_token = result.refresh_token, expires_in = result.expires_in, scope = result.scope;
|
||||
|
|
@ -130,7 +144,11 @@ var WechatPublicInstance = /** @class */ (function () {
|
|||
var result, nickname, sex, headimgurl;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, this.access("https://api.weixin.qq.com/sns/userinfo?access_token=".concat(accessToken, "&openid=").concat(openId, "&lang=zh_CN"), { nickname: '码农哥', sex: 1, headimgurl: 'https://www.ertongzy.com/uploads/allimg/161005/2021233Y7-0.jpg' })];
|
||||
case 0: return [4 /*yield*/, this.access("https://api.weixin.qq.com/sns/userinfo?access_token=".concat(accessToken, "&openid=").concat(openId, "&lang=zh_CN"), {
|
||||
nickname: '码农哥',
|
||||
sex: 1,
|
||||
headimgurl: 'https://www.ertongzy.com/uploads/allimg/161005/2021233Y7-0.jpg',
|
||||
})];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
nickname = result.nickname, sex = result.sex, headimgurl = result.headimgurl;
|
||||
|
|
@ -437,6 +455,83 @@ var WechatPublicInstance = /** @class */ (function () {
|
|||
});
|
||||
});
|
||||
};
|
||||
WechatPublicInstance.prototype.getTicket = function () {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var myInit, token, result, ticket;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
myInit = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
return [4 /*yield*/, this.getAccessToken()];
|
||||
case 1:
|
||||
token = _a.sent();
|
||||
return [4 /*yield*/, this.access("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=".concat(token, "&type=jsapi"), {
|
||||
ticket: "ticket".concat(Date.now()),
|
||||
expires_in: 30,
|
||||
}, myInit)];
|
||||
case 2:
|
||||
result = (_a.sent());
|
||||
ticket = result.ticket;
|
||||
return [2 /*return*/, ticket];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
WechatPublicInstance.prototype.randomString = function () {
|
||||
var len = 16;
|
||||
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
|
||||
/** **默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
||||
var maxPos = $chars.length;
|
||||
var pwd = '';
|
||||
for (var i = 0; i < len; i++) {
|
||||
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return pwd;
|
||||
};
|
||||
WechatPublicInstance.prototype.signatureJsSDK = function (options) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var url, noncestr, timestamp, jsapi_ticket, contentArray, zhimaString;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = options.url;
|
||||
noncestr = this.randomString();
|
||||
timestamp = parseInt((Date.now() / 1000).toString(), 10);
|
||||
return [4 /*yield*/, this.getTicket()];
|
||||
case 1:
|
||||
jsapi_ticket = _a.sent();
|
||||
contentArray = {
|
||||
noncestr: noncestr,
|
||||
jsapi_ticket: jsapi_ticket,
|
||||
timestamp: timestamp,
|
||||
url: url,
|
||||
};
|
||||
zhimaString = '';
|
||||
Object.keys(contentArray)
|
||||
.sort()
|
||||
.forEach(function (ele, idx) {
|
||||
if (idx > 0) {
|
||||
zhimaString += '&';
|
||||
}
|
||||
zhimaString += ele;
|
||||
zhimaString += '=';
|
||||
zhimaString += contentArray[ele];
|
||||
});
|
||||
return [2 /*return*/, {
|
||||
signature: sha1(zhimaString),
|
||||
noncestr: noncestr,
|
||||
timestamp: timestamp,
|
||||
appId: this.appId,
|
||||
}];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
return WechatPublicInstance;
|
||||
}());
|
||||
exports.WechatPublicInstance = WechatPublicInstance;
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@
|
|||
"@types/node": "^17.0.31",
|
||||
"ts-node": "~10.9.1",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "~4.7.4"
|
||||
"typescript": "~4.7.4",
|
||||
"@types/sha1": "^1.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.12",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"sha1": "^1.1.1",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.525",
|
||||
"ts-md5": "^1.3.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
require('../../fetch');
|
||||
import crypto from 'crypto';
|
||||
import { Buffer } from 'buffer';
|
||||
const sha1 = require('sha1');
|
||||
|
||||
// 目前先支持text和news, 其他type文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Service_Center_messages.html
|
||||
// type ServeMessageType = 'text' | 'news' | 'mpnews' | 'mpnewsarticle' | 'image' | 'voice' | 'video' | 'music' | 'msgmenu';/
|
||||
type TextServeMessageOption = {
|
||||
|
|
@ -38,19 +40,23 @@ export class WechatPublicInstance {
|
|||
private refreshAccessTokenHandler?: any;
|
||||
private externalRefreshFn?: (appId: string) => Promise<string>;
|
||||
|
||||
constructor(appId: string, appSecret?: string, accessToken?: string, externalRefreshFn?: (appId: string) => Promise<string>) {
|
||||
constructor(
|
||||
appId: string,
|
||||
appSecret?: string,
|
||||
accessToken?: string,
|
||||
externalRefreshFn?: (appId: string) => Promise<string>
|
||||
) {
|
||||
this.appId = appId;
|
||||
this.appSecret = appSecret;
|
||||
|
||||
this.externalRefreshFn = externalRefreshFn;
|
||||
if(!appSecret && !externalRefreshFn) {
|
||||
if (!appSecret && !externalRefreshFn) {
|
||||
throw new Error('appSecret和externalRefreshFn必须至少支持一个');
|
||||
}
|
||||
|
||||
|
||||
if (accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.refreshAccessToken();
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +71,11 @@ export class WechatPublicInstance {
|
|||
}
|
||||
}
|
||||
|
||||
private async access(url: string, mockData: any, init?: RequestInit): Promise<any> {
|
||||
private async access(
|
||||
url: string,
|
||||
mockData: any,
|
||||
init?: RequestInit
|
||||
): Promise<any> {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return mockData;
|
||||
}
|
||||
|
|
@ -107,10 +117,25 @@ export class WechatPublicInstance {
|
|||
async code2Session(code: string) {
|
||||
const result = await this.access(
|
||||
`https://api.weixin.qq.com/sns/oauth2/access_token?appid=${this.appId}&secret=${this.appSecret}&code=${code}&grant_type=authorization_code`,
|
||||
{ access_token: 'aaa', openid: code, unionid: code, refresh_token: 'aaa', is_snapshotuser: false, expires_in: 30, scope: 'userinfo' }
|
||||
{
|
||||
access_token: 'aaa',
|
||||
openid: code,
|
||||
unionid: code,
|
||||
refresh_token: 'aaa',
|
||||
is_snapshotuser: false,
|
||||
expires_in: 30,
|
||||
scope: 'userinfo',
|
||||
}
|
||||
);
|
||||
const { access_token, openid, unionid, scope, refresh_token, is_snapshotuser, expires_in } =
|
||||
typeof result === 'string' ? JSON.parse(result) : result; // 这里微信返回的数据有时候竟然是text/plain
|
||||
const {
|
||||
access_token,
|
||||
openid,
|
||||
unionid,
|
||||
scope,
|
||||
refresh_token,
|
||||
is_snapshotuser,
|
||||
expires_in,
|
||||
} = typeof result === 'string' ? JSON.parse(result) : result; // 这里微信返回的数据有时候竟然是text/plain
|
||||
|
||||
return {
|
||||
accessToken: access_token as string,
|
||||
|
|
@ -127,7 +152,12 @@ export class WechatPublicInstance {
|
|||
async refreshUserAccessToken(refreshToken: string) {
|
||||
const result = await this.access(
|
||||
`https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=${this.appId}&grant_type=refresh_token&refresh_token=${refreshToken}`,
|
||||
{ access_token: 'aaa', refresh_token: 'aaa', expires_in: 30, scope: 'userinfo' }
|
||||
{
|
||||
access_token: 'aaa',
|
||||
refresh_token: 'aaa',
|
||||
expires_in: 30,
|
||||
scope: 'userinfo',
|
||||
}
|
||||
);
|
||||
const { access_token, refresh_token, expires_in, scope } = result;
|
||||
return {
|
||||
|
|
@ -141,21 +171,28 @@ export class WechatPublicInstance {
|
|||
async getUserInfo(accessToken: string, openId: string) {
|
||||
const result = await this.access(
|
||||
`https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openId}&lang=zh_CN`,
|
||||
{ nickname: '码农哥', sex: 1, headimgurl: 'https://www.ertongzy.com/uploads/allimg/161005/2021233Y7-0.jpg' }
|
||||
{
|
||||
nickname: '码农哥',
|
||||
sex: 1,
|
||||
headimgurl:
|
||||
'https://www.ertongzy.com/uploads/allimg/161005/2021233Y7-0.jpg',
|
||||
}
|
||||
);
|
||||
const { nickname, sex, headimgurl } = result;
|
||||
return {
|
||||
nickname: nickname as string,
|
||||
gender: sex === 1 ? 'male' : sex === 2 ?'female' : undefined,
|
||||
gender: sex === 1 ? 'male' : sex === 2 ? 'female' : undefined,
|
||||
avatar: headimgurl as string,
|
||||
};
|
||||
}
|
||||
|
||||
private async refreshAccessToken(url?: string, init?: RequestInit) {
|
||||
const result = this.externalRefreshFn ? await this.externalRefreshFn(this.appId) : await this.access(
|
||||
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.appId}&secret=${this.appSecret}`,
|
||||
{ access_token: 'mockToken', expires_in: 600 }
|
||||
);
|
||||
const result = this.externalRefreshFn
|
||||
? await this.externalRefreshFn(this.appId)
|
||||
: await this.access(
|
||||
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.appId}&secret=${this.appSecret}`,
|
||||
{ access_token: 'mockToken', expires_in: 600 }
|
||||
);
|
||||
const { access_token, expires_in } = result;
|
||||
this.accessToken = access_token;
|
||||
// 生成下次刷新的定时器
|
||||
|
|
@ -428,4 +465,74 @@ export class WechatPublicInstance {
|
|||
}
|
||||
throw new Error(JSON.stringify(result));
|
||||
}
|
||||
|
||||
async getTicket() {
|
||||
const myInit = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
const token = await this.getAccessToken();
|
||||
const result = (await this.access(
|
||||
`https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${token}&type=jsapi`,
|
||||
{
|
||||
ticket: `ticket${Date.now()}`,
|
||||
expires_in: 30,
|
||||
},
|
||||
myInit
|
||||
)) as {
|
||||
ticket: string;
|
||||
expires_in: number;
|
||||
};
|
||||
|
||||
const { ticket } = result;
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
private randomString() {
|
||||
let len = 16;
|
||||
let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
|
||||
|
||||
/** **默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
||||
let maxPos = $chars.length;
|
||||
let pwd = '';
|
||||
for (let i = 0; i < len; i++) {
|
||||
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return pwd;
|
||||
}
|
||||
|
||||
async signatureJsSDK(options: { url: string }) {
|
||||
const url = options.url;
|
||||
|
||||
const noncestr = this.randomString();
|
||||
const timestamp = parseInt((Date.now() / 1000).toString(), 10);
|
||||
|
||||
const jsapi_ticket = await this.getTicket();
|
||||
const contentArray = {
|
||||
noncestr,
|
||||
jsapi_ticket,
|
||||
timestamp,
|
||||
url,
|
||||
};
|
||||
let zhimaString = '';
|
||||
Object.keys(contentArray)
|
||||
.sort()
|
||||
.forEach((ele, idx) => {
|
||||
if (idx > 0) {
|
||||
zhimaString += '&';
|
||||
}
|
||||
zhimaString += ele;
|
||||
zhimaString += '=';
|
||||
zhimaString += contentArray[ele as keyof typeof contentArray];
|
||||
});
|
||||
return {
|
||||
signature: sha1(zhimaString),
|
||||
noncestr,
|
||||
timestamp,
|
||||
appId: this.appId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue