|
@@ -0,0 +1,145 @@
|
|
|
|
+'use strict';
|
|
|
|
+
|
|
|
|
+const assert = require('assert');
|
|
|
|
+const uuid = require('uuid');
|
|
|
|
+const _ = require('lodash');
|
|
|
|
+const { BusinessError, ErrorCode } = require('naf-core').Error;
|
|
|
|
+const crypto = require('crypto');
|
|
|
|
+const Service = require('egg').Service;
|
|
|
|
+
|
|
|
|
+class WeixinAuthService extends Service {
|
|
|
|
+ constructor(ctx) {
|
|
|
|
+ super(ctx, {});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 取得微信AccessToken
|
|
|
|
+ async accesstoken({ appid }) {
|
|
|
|
+ assert(appid, '缺少appid参数项');
|
|
|
|
+ const key = `visit:auth:accesstoken:${appid}`;
|
|
|
|
+ const val = await this.app.redis.get(key);
|
|
|
|
+ const data = {};
|
|
|
|
+ if (val) {
|
|
|
|
+ const { access_token } = JSON.parse(val);
|
|
|
|
+ data.access_token = access_token;
|
|
|
|
+ } else {
|
|
|
|
+ const { wxapi } = this.app.config;
|
|
|
|
+ const appidlist = wxapi;
|
|
|
|
+ const wxapp = _.find(appidlist, { appid });
|
|
|
|
+ const feturl = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${wxapp.appSecret}`;
|
|
|
|
+ const result = await this.ctx.curl(feturl, {
|
|
|
|
+ method: 'get',
|
|
|
|
+ headers: {
|
|
|
|
+ 'content-type': 'application/json',
|
|
|
|
+ },
|
|
|
|
+ dataType: 'json',
|
|
|
|
+ });
|
|
|
|
+ if (result) {
|
|
|
|
+ console.log(result);
|
|
|
|
+ const val = JSON.stringify(result.data);
|
|
|
|
+ await this.app.redis.set(key, val, 'EX', 7200);
|
|
|
|
+ data.access_token = result.data.access_token;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return data;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 取得微信jsapiticket
|
|
|
|
+ async jsapiticket({ appid }) {
|
|
|
|
+ assert(appid, '缺少appid参数项');
|
|
|
|
+ const result = await this.accesstoken({ appid });
|
|
|
|
+ const accesstoken = result.access_token;
|
|
|
|
+ console.log(accesstoken);
|
|
|
|
+ assert(accesstoken, '缺少access_token参数项');
|
|
|
|
+ const key = `visit:auth:jsapiticket:${appid}`;
|
|
|
|
+ const val = await this.app.redis.get(key);
|
|
|
|
+ const data = {};
|
|
|
|
+ if (val) {
|
|
|
|
+ const { ticket } = JSON.parse(val);
|
|
|
|
+ data.ticket = ticket;
|
|
|
|
+ } else {
|
|
|
|
+ const feturl = `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${accesstoken}&type=jsapi`;
|
|
|
|
+ const result = await this.ctx.curl(feturl, {
|
|
|
|
+ method: 'get',
|
|
|
|
+ headers: {
|
|
|
|
+ 'content-type': 'application/json',
|
|
|
|
+ },
|
|
|
|
+ dataType: 'json',
|
|
|
|
+ });
|
|
|
|
+ if (result) {
|
|
|
|
+ console.log(result);
|
|
|
|
+ if (result.data.errcode === 0) {
|
|
|
|
+ const val = JSON.stringify(result.data);
|
|
|
|
+ await this.app.redis.set(key, val, 'EX', 7200);
|
|
|
|
+ data.ticket = result.data.ticket;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return data;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 签名算法
|
|
|
|
+ async getsign({ appid, url }) {
|
|
|
|
+ assert(appid, '缺少appid参数项');
|
|
|
|
+ assert(url, '缺少url参数项');
|
|
|
|
+ const result = await this.jsapiticket({ appid });
|
|
|
|
+ // const { wxapi } = this.app.config;
|
|
|
|
+ // const appidlist = wxapi;
|
|
|
|
+ // const wxapp = _.find(appidlist, { appid });
|
|
|
|
+ console.log(url);
|
|
|
|
+ const noncestr = await this.createNonceStr();
|
|
|
|
+ const timestamp = await this.createTimestamp();
|
|
|
|
+ const ret = {
|
|
|
|
+ jsapi_ticket: result.ticket,
|
|
|
|
+ noncestr,
|
|
|
|
+ timestamp,
|
|
|
|
+ url,
|
|
|
|
+ };
|
|
|
|
+ console.log(ret);
|
|
|
|
+ const string = await this.raw(ret);
|
|
|
|
+ ret.sign = await this.sha1(string);
|
|
|
|
+ ret.appid = appid;
|
|
|
|
+ console.log('ret', ret);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // sha1加密
|
|
|
|
+ async sha1(str) {
|
|
|
|
+ const shasum = crypto.createHash('sha1');
|
|
|
|
+ shasum.update(str);
|
|
|
|
+ str = shasum.digest('hex');
|
|
|
|
+ return str;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 生成签名的时间戳
|
|
|
|
+ async createTimestamp() {
|
|
|
|
+ return parseInt(new Date().getTime() / 1000) + '';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 生成签名的随机串
|
|
|
|
+ async createNonceStr() {
|
|
|
|
+ return Math.random().toString(36).substr(2, 15);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 对参数对象进行字典排序
|
|
|
|
+ // @param {对象} args 签名所需参数对象
|
|
|
|
+ // @return {字符串} 排序后生成字符串
|
|
|
|
+ async raw(args) {
|
|
|
|
+ let keys = Object.keys(args);
|
|
|
|
+ keys = keys.sort();
|
|
|
|
+ const newArgs = {};
|
|
|
|
+ keys.forEach(function(key) {
|
|
|
|
+ newArgs[key.toLowerCase()] = args[key];
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ let string = '';
|
|
|
|
+ for (const k in newArgs) {
|
|
|
|
+ string += '&' + k + '=' + newArgs[k];
|
|
|
|
+ }
|
|
|
|
+ string = string.substr(1);
|
|
|
|
+ return string;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+module.exports = WeixinAuthService;
|