首页 > 编程语言 >node服务器接入微信与企业微信js-sdk 第一篇

node服务器接入微信与企业微信js-sdk 第一篇

时间:2023-02-09 14:34:36浏览次数:45  
标签:node access const 微信 js tokens token ticket type

目录结构

image.png

1.main.config.js

module.exports = {
  corp_id: "ww4ae3cf8d6688c7e6",
  agent_id: "1000002",
  app_secret: "2b4Ae55a4t58ibkZp09_lyxGF1nwLBiPSYR15kxyIQk",

  appid: "wx6794d4a3b28cc467",
  wx_secret: "2d52e787a18f952e70ffa34a1864762e",
  jsApiList: [
    "checkJsApi",
    "updateAppMessageShareData",
    "updateTimelineShareData",
    "onMenuShareTimeline",
    "onMenuShareAppMessage",
    "onMenuShareQQ",
    "onMenuShareWeibo",
    "hideMenuItems",
    "showMenuItems",
    "hideAllNonBaseMenuItem",
    "showAllNonBaseMenuItem",
    "translateVoice",
    "startRecord",
    "stopRecord",
    "onRecordEnd",
    "playVoice",
    "pauseVoice",
    "stopVoice",
    "uploadVoice",
    "downloadVoice",
    "chooseImage",
    "previewImage",
    "uploadImage",
    "downloadImage",
    "getNetworkType",
    "openLocation",
    "getLocation",
    "hideOptionMenu",
    "showOptionMenu",
    "closeWindow",
    "scanQRCode",
    "chooseWXPay",
    "openProductSpecificView",
    "addCard",
    "chooseCard",
    "openCard",
  ],
}

2.signApi.js

/**
 * 获取签名
 * @returns:
 * 1. appId 必填,公众号的唯一标识
 * 2. timestamp 必填,生成签名的时间戳
 * 3. nonceStr 必填,生成签名的随机串
 * 4. signature 必填,签名
 */
const crypto = require("crypto");

// sha1加密
function sha1(str) {
  let shasum = crypto.createHash("sha1");
  shasum.update(str);
  str = shasum.digest("hex");
  return str;
}

/**
 * 生成签名的时间戳
 * @return {字符串}
 */
function createTimestamp() {
  return parseInt(new Date().getTime() / 1000) + "";
}

/**
 * 生成签名的随机串
 * @return {字符串}
 */
function createNonceStr() {
  return Math.random().toString(36).substr(2, 15);
}

/**
 * 对参数对象进行字典排序
 * @param  {对象} args 签名所需参数对象
 * @return {字符串}    排序后生成字符串
 */
function raw(args) {
  var keys = Object.keys(args);
  keys = keys.sort();
  var newArgs = {};
  keys.forEach(function (key) {
    newArgs[key.toLowerCase()] = args[key];
  });

  var string = "";
  for (var k in newArgs) {
    string += "&" + k + "=" + newArgs[k];
  }
  string = string.substr(1);
  return string;
}

module.exports = getSign = ({ params, res, otherParams }) => {
  /**
   * 签名算法
   * 签名生成规则如下:
   * 参与签名的字段包括noncestr( 随机字符串),
   * 有效的jsapi_ticket, timestamp( 时间戳),
   * url( 当前网页的URL, 不包含# 及其后面部分)。
   * 对所有待签名参数按照字段名的ASCII 码从小到大排序( 字典序) 后,
   *  使用URL键值对的格式( 即key1 = value1 & key2 = value2…) 拼接成字符串string1。
   * 这里需要注意的是所有参数名均为小写字符。 对string1作sha1加密, 字段名和字段值都采用原始值, 不进行URL 转义。
   */
  var ret = {
    jsapi_ticket: params.ticket,
    nonceStr: createNonceStr(),
    timestamp: createTimestamp(),
    url: params.url,
  };
  // console.log(params, ret);
  var string = raw(ret);

  var resMap = {
    ...otherParams,
    debug: false,
    nonceStr: ret.nonceStr,
    signature: sha1(string),
    timestamp: ret.timestamp,
  };
  console.log("resMap", resMap);
  res.send({
    data: resMap,
    success: true,
  });
};

3.tokens.json

{}

4.wxtokens.json

{}

5.accesstoken.js

const axios = require("axios");
const Config = require("../configs/main.config");
const fs = require("fs");
const path = require("path");
const { debounce } = require("lodash");
let flag = false;

// token 缓存文件存储位置
const token_path = "../temp/tokens.json";
const wxtoken_path = "../temp/wxtokens.json";

// 判断 token 是否过期
let _isExpire = function (create_time, expire_time) {
  const current = Math.floor(Date.now() / 1000);
  return create_time + expire_time < current;
};

let getSaveWXToken = async function ({ tokens, type, appid, secret }) {
  try {
    // console.log("getSaveWXToken", tokens, type, appid, secret);
    const {
      data: { access_token, expires_in },
    } = await axios.get("https://api.weixin.qq.com/cgi-bin/token", {
      params: {
        grant_type: "client_credential",
        appid: appid,
        secret: secret,
      },
    });
    // 重新写入 access_token
    tokens[type] = {};
    tokens[type].create_time = Math.floor(Date.now() / 1000);
    tokens[type].expire_time = expires_in;
    tokens[type].token = access_token;
    fs.writeFileSync(
      path.join(__dirname, wxtoken_path),
      JSON.stringify(tokens),
      {
        encoding: "utf-8",
      }
    );
    // 返回 access_token
    console.log(`获取WX ${type} access_token 成功`, access_token);
    return access_token;
  } catch (error) {
    console.log("error", error);
  }
};

const debounceSaveWXToken = debounce(getSaveWXToken, 1000 * 60 * 5);

// 获取 QYWXtoken
let _getQYWXTokenWithType = async function (type) {
  let tokens = {};
  let corp_id = Config.corp_id;
  //
  if (!type) {
    type = "app";
  }
  let secret = Config[`${type}_secret`];

  // 先尝试从缓存中读取出token
  try {
    tokens = JSON.parse(
      fs.readFileSync(path.join(__dirname, token_path), {
        encoding: "utf-8",
      })
    );
  } catch (err) {
    // 缓存文件读取失败
    console.error(err);
  }

  if (
    !tokens[type] ||
    !tokens[type].token ||
    _isExpire(tokens[type].create_time, tokens[type].expire_time)
  ) {
    // 如果缓存中没有 token,或者 token 过期
    console.log(`重新获取QYWX ${type} access_token`);
    // 发起请求,获取 access_token
    const {
      data: { access_token, expires_in },
    } = await axios.get("https://qyapi.weixin.qq.com/cgi-bin/gettoken", {
      params: {
        corpid: corp_id,
        corpsecret: secret,
      },
    });
    // 重新写入 access_token
    tokens[type] = {};
    tokens[type].create_time = Math.floor(Date.now() / 1000);
    tokens[type].expire_time = expires_in;
    tokens[type].token = access_token;
    fs.writeFileSync(path.join(__dirname, token_path), JSON.stringify(tokens), {
      encoding: "utf-8",
    });
    // 返回 access_token
    console.log(`获取QYWX ${type} access_token 成功`, access_token);
    return access_token;
  } else {
    // 从缓存中读取
    console.log(`从缓存中读取QYWX ${type} access_token`, tokens[type].token);
    return tokens[type].token;
  }
};

// 获取 QYWXjsapi_ticket 临时票据
let _getQYWXTicketWithType = async function (type) {
  let tokens = {};
  let ACCESS_TOKEN = await _getQYWXTokenWithType("app");
  //
  if (!type) {
    type = "ticket";
  }

  // 先尝试从缓存中读取出token
  try {
    tokens = JSON.parse(
      fs.readFileSync(path.join(__dirname, token_path), {
        encoding: "utf-8",
      })
    );
  } catch (err) {
    // 缓存文件读取失败
    console.error(err);
  }

  if (
    !tokens[type] ||
    !tokens[type].ticket ||
    _isExpire(tokens[type].create_time, tokens[type].expire_time)
  ) {
    // 如果缓存中没有 token,或者 token 过期
    console.log(`重新获取QYWX ${type} access_token`);
    // 发起请求,获取 access_token
    const {
      data: { ticket, expires_in },
    } = await axios.get("https://qyapi.weixin.qq.com/cgi-bin/ticket/get", {
      params: {
        type: "agent_config",
        access_token: ACCESS_TOKEN,
      },
    });
    // 重新写入 access_token
    tokens[type] = {};
    tokens[type].create_time = Math.floor(Date.now() / 1000);
    tokens[type].expire_time = expires_in;
    tokens[type].ticket = ticket;
    fs.writeFileSync(path.join(__dirname, token_path), JSON.stringify(tokens), {
      encoding: "utf-8",
    });
    // 返回 access_token
    console.log(`获取QYWX ${type} access_token 成功`, ticket);
    return ticket;
  } else {
    // 从缓存中读取
    console.log(`从缓存中读取QYWX ${type} access_token`, tokens[type].ticket);
    return tokens[type].ticket;
  }
};

// 获取 WXtoken
let _getWXTokenWithType = async function (type) {
  let tokens = {};
  let appid = Config.appid;
  //
  if (!type) {
    type = "app";
  }
  let secret = Config[`${type}_secret`];

  // 先尝试从缓存中读取出token
  try {
    tokens = JSON.parse(
      fs.readFileSync(path.join(__dirname, wxtoken_path), {
        encoding: "utf-8",
      })
    );
  } catch (err) {
    // 缓存文件读取失败
    console.error(err);
  }

  if (
    !tokens[type] ||
    !tokens[type].token ||
    _isExpire(tokens[type].create_time, tokens[type].expire_time)
  ) {
    // 如果缓存中没有 token,或者 token 过期
    console.log(`重新获取WX ${type} WXToken`);
    // 发起请求,获取 access_token
    const access_token = await getSaveWXToken({
      tokens,
      type,
      appid,
      secret,
    });
    return access_token;
  } else {
    // 从缓存中读取
    console.log(`从缓存中读取WX ${type} WXToken`, tokens[type].token);
    let access_token = tokens[type].token;
    const { data } = await axios.get(
      `https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=${access_token}`
    );
    // console.log("data", data);
    if (data.errcode && data.errcode == 40001) {
      if (!flag) {
        flag = true;
        access_token = getSaveWXToken({ tokens, type, appid, secret });
        return access_token;
      }
      access_token = debounceSaveWXToken({ tokens, type, appid, secret });
      return access_token || tokens[type].token;
    }
    // getSaveWXToken({ tokens, type, appid, secret });
    return tokens[type].token;
  }
};

// 获取 WXjsapi_ticket 临时票据
let _getWXTicketWithType = async function (type) {
  let tokens = {};
  let ACCESS_TOKEN = await _getWXTokenWithType("wx");
  //
  if (!type) {
    type = "app";
  }

  // 先尝试从缓存中读取出token
  try {
    tokens = JSON.parse(
      fs.readFileSync(path.join(__dirname, wxtoken_path), {
        encoding: "utf-8",
      })
    );
  } catch (err) {
    // 缓存文件读取失败
    console.error(err);
  }

  if (
    !tokens[type] ||
    !tokens[type].ticket ||
    _isExpire(tokens[type].create_time, tokens[type].expire_time)
  ) {
    // 如果缓存中没有 ticket,或者 ticket 过期
    console.log(`重新获取WX ${type} WXTicket_token`);
    // 发起请求,获取 access_token
    const {
      data: { ticket, expires_in },
    } = await axios.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket", {
      params: {
        type: "jsapi",
        access_token: ACCESS_TOKEN,
      },
    });
    // 重新写入 access_token
    tokens[type] = {};
    tokens[type].create_time = Math.floor(Date.now() / 1000);
    tokens[type].expire_time = expires_in;
    tokens[type].ticket = ticket;
    fs.writeFileSync(
      path.join(__dirname, wxtoken_path),
      JSON.stringify(tokens),
      {
        encoding: "utf-8",
      }
    );
    // 返回 access_token
    console.log(`获取WX ${type} access_token 成功`, ticket);
    return ticket;
  } else {
    // 从缓存中读取
    console.log(`从缓存中读取WX ${type} WXTicket_token`, tokens[type].ticket);
    return tokens[type].ticket;
  }
};

module.exports = {
  // 获取普通应用的 QYWXtoken
  async getAppToken() {
    return await _getQYWXTokenWithType("app");
  },
  // 获取普通应用的 QYWXjsapi_ticket 临时票据
  async getAppTicket() {
    return await _getQYWXTicketWithType("ticket");
  },
  // 获取普通应用的 WXtoken
  async getWXAppToken() {
    return await _getWXTokenWithType("wx");
  },
  // 获取普通应用的 WXjsapi_ticket 临时票据
  async getWXAppTicket() {
    return await _getWXTicketWithType("ticket");
  },
};

6.utilController.js

const AccessToken = require("./accesstoken");
const config = require("../configs/main.config");
const signApi = require("../routes/signApi");
module.exports = function (router) {
  //获取微信签名
  router.get("/wechat-config", async (req, res) => {
    try {
      const params = {};
      console.log(req.query);
      params.url = req.query.url;
      params.ticket = await AccessToken.getWXAppTicket();
      const otherParams = {
        appId: config.appid,
        jsApiList: config.jsApiList,
      };
      signApi({ params, res, otherParams });
    } catch (error) {
      res.send({
        success: false,
      });
    }
  });

  //获取QYWX签名
  router.get("/qywechat-config", async (req, res) => {
    try {
      const params = {};
      console.log(req.query);
      params.url = req.query.url;
      params.ticket = await AccessToken.getAppTicket();
      const otherParams = {
        corpid: config.corp_id,
        agentid: config.agent_id,
        jsApiList: config.jsApiList,
      };
      signApi({ params, res, otherParams });
    } catch (error) {
      res.send({
        success: false,
      });
    }
  });
}

7.users.js

var express = require("express");
var router = express.Router();

const utilController = require("../server/utilController");

utilController(router);

module.exports = router;

8.app.js

var express = require("express");
var usersRouter = require("./routes/users");

var app = express();
app.use("/apiWx", usersRouter);
module.exports = app;

9.www

var app = require("../app");
var http = require("http");

var port = normalizePort(process.env.PORT || "1024");
app.set("port", port);
var server = http.createServer(app);
server.listen(port);
server.on("listening", () => onListening(server));
function onListening(server) {
  var addr = server.address();
  var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
  debug("Listening on " + bind);
}

10.package.json

**启动重点 **"start": "nodemon ./bin/index.js"

{
  "name": "internalappsample",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "nodemon ./bin/index.js",
    "serve": "cd ./vue && yarn serve",
    "build": "cd ./vue && npx yarn install && npx yarn build"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "cookie-parser": "~1.4.4",
    "cors": "^2.8.5",
    "debug": "~2.6.9",
    "ejs": "^3.1.8",
    "express": "~4.16.1",
    "express-fileupload": "^1.2.1",
    "express-session": "^1.17.3",
    "form-data": "^4.0.0",
    "http-errors": "~1.6.3",
    "jade": "~1.11.0",
    "lodash": "^4.17.21",
    "morgan": "~1.9.1",
    "request": "^2.88.2",
    "url-parse": "^1.5.3"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.29.3",
    "@typescript-eslint/parser": "^4.29.3",
    "eslint": "^7.32.0",
    "eslint-plugin-vue": "^7.16.0",
    "typescript": "^4.3.5"
  }
}

                                                                                             作者:白马不是马

标签:node,access,const,微信,js,tokens,token,ticket,type
From: https://www.cnblogs.com/DTCLOUD/p/17105157.html

相关文章

  • threejs 第三十八用 MeshToonMaterial
    这个MeshToonMaterial材质好像让画面有点动画的感觉  threejs交流群511163089......
  • threejs 第二十用 shaderMaterial
    自己写shader就得用这个材质需要vertexfragment先<scriptid="vertex-Shader"type="x-shader/x-vertex">varyingvec2vUv;voidmain(){gl_P......
  • js 判断条件分支优化
    优化前:  1.简单分支优化:  2.复杂分支优化:    3.抽离分支:   ......
  • js截取指定字符前面或后面的内容
    getCaption(obj,state,strname){varindex=obj.lastIndexOf(strname);if(state==0){obj=obj.substring(0,index)......
  • json.dumps
    separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了。1234567891011impor......
  • JS新运算符 ?. ?? ??= ||= &&= 的含义及用法
    ?.可选链操作符可选链允许读取连接对象链深处的属性值而不必明确验证链中每个引用是否有效。该表达式短路返回值语法:obj?.propobj?.[expr]arr?.[index]func......
  • 浅谈JS词法环境
    JavaScript词法环境本文主要讲解JS词法环境,我们将看到什么是词法环境,词法范围如何工作,函数内部的名称如何解析,内部属性,弄清楚词法环境利于我们理解闭包。让我们开始吧.........
  • 《Vue.js 设计与实现》读书笔记 - 第14章、内建组件和模块
    第14章、内建组件和模块14.1KeepAlive组件的实现原理KeepAlive一词借鉴了HTTP协议。KeepAlive组件可以避免组件被频繁的销毁/重建。本质是缓存管理,再加上特殊的挂......
  • 微信开放平台之第三方平台开发,模板小程序如何提交?
    大家好,我是悟空码字今天天气晴朗,阳光普照。因为疫情影响,小羊人的增多,街上放眼望去,人烟稀少。楼下除了几个十一二岁的小男孩在玩耍,也没有像往日老人悠闲打牌、小孩嬉戏那般......
  • 原生JS实现涟漪按钮特效
    给大家分享一个用原生JS实现的涟漪按钮特效,效果如下:以下是代码实现,欢迎大家复制粘贴和收藏。<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8">......