>  기사  >  웹 프론트엔드  >  WeChat JS-SDK 백엔드 인터페이스에서 Node.js 구현

WeChat JS-SDK 백엔드 인터페이스에서 Node.js 구현

巴扎黑
巴扎黑원래의
2017-07-18 17:57:451474검색

웹사이트를 만들고, 온라인에 올리고, 위챗으로 열고 클릭해서 공유했는데, 공유 후 친구에게 보내는 링크 카드는 기본적으로 위챗과 함께 오는 카드입니다.
WeChat JS-SDK 백엔드 인터페이스에서 Node.js 구현


제목, 설명, 사진은 기본으로 되어있습니다. 보기 흉한 것은 말할 것도 없고, 다른 사람들에게 공유해서 계정 도용 웹사이트인줄 알았습니다. 그러나 WeChat의 JSSDK에 접속한 후에는 다음과 같이 공유 내용을 사용자 정의할 수 있습니다.
WeChat JS-SDK 백엔드 인터페이스에서 Node.js 구현


이 공유의 제목과 내용이 동일하지는 않지만 심각하지는 않지만 표현을 방해하지는 않습니다. WeChat JSSDK를 통해 콘텐츠를 정의하고 공유할 수 있습니다. 백엔드 Node.js의 JSSDK를 단계별로 살펴봅니다.

인터페이스 구성 정보

인터페이스 구성 정보 수정

  1. URL 주소는 서버의 주소여야 하며, 이 주소는 브라우저의 주소 표시줄을 통해 접근할 수 있어야 합니다. (서버가 없더라도 상관없습니다. 나중에 하나 만들어 보겠습니다.)
    Assumption 제가 여기에 입력한 서버 주소는 "http://www.your_server_name.com/wxJssdk"

  2. Token입니다. 마음대로 입력하여 서명을 생성하는 데 사용할 수 있습니다. (서명을 모르시나요? 상관없습니다. 나중에 사용하게 됩니다.)
    여기에 입력한 토큰이 "jegfjaeghfuccawegfgjdbh"라고 가정해 보겠습니다.

여기서 제출을 클릭하면 구성이 실패하게 됩니다. 제출할 때 WeChat은 서버 주소를 요청하며 현재 구성된 주소는 액세스할 수 없으므로 구성이 실패했다는 메시지가 표시됩니다. 하지만 걱정하지 마세요. WeChat이 서버에 액세스할 수 있도록 먼저 간단한 Node 서버를 구축해 보겠습니다.

간단한 노드 서버 구축

이 도메인 이름에 서버를 구축하고 인터페이스를 /wxJssdk/wxJssdk

const express = require('express')const app = express()app.get('/wxJssdk', (req, res) => {
  res.send('请求成功了了了了')})app.listen(80, err => {
  if(!err) console.log('connect succeed')})

现在我们在地址栏中访问 ,如果页面显示“请求成功了了了了”,则进入到下一步,如果没有成功的话,检查一下你的服务器是否开启Node服务器,如:node index.js

const express = require('express')const app = express()const sha1 = require('sha1')app.get('/wxJssdk', (req, res) => {
  let wx = req.query

  let token = 'jegfjaeghfuccawegfgjdbh'
  let timestamp = wx.timestamp
  let nonce = wx.nonce

  // 1)将token、timestamp、nonce三个参数进行字典序排序
  let list = [token, timestamp, nonce].sort()  // 2)将三个参数字符串拼接成一个字符串进行sha1加密
  let str = list.join('')  let result = sha1(str)  // 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
  if (result === wx.signature) {res.send(wx.echostr) // 返回微信传来的echostr,表示校验成功,此处不能返回其它
  } else {res.send(false)  }})
로 노출해야 합니다. 주소 표시줄에서 해당 페이지에 액세스합니다. 페이지에 "요청이 성공했습니다"라고 표시되면 다음 단계로 이동합니다. 성공하지 못한 경우 서버에 노드 인덱스와 같은 노드 서버가 활성화되어 있는지 확인합니다. .js<p></p>이때 WeChat 테스트 공식 계정 백엔드의 인터페이스 구성 정보를 저장할 때 여전히 구성에 실패했다는 메시지가 표시됩니다. 이는 요구 사항에 따라 반환되지 않았기 때문입니다. <h3></h3>WeChat 테스트 공개 계정 요청 정보를 기반으로 해당 콘텐츠를 반환합니다<p></p>WeChat 공개 계정 개발 문서 액세스 가이드에 따르면 WeChat이 구성한 인터페이스를 요청할 때 다음 정보를 가져옵니다<table> <thead><tr class="header firstRow"> <th align="left"></th>Parameters<th align="left"></th> Description</tr></thead> <tbody> <tr class="odd"> <td align="left"></td>signature<td align="left"></td>WeChat 암호화 서명인 서명은 개발자가 입력한 토큰 매개변수와 요청의 타임스탬프 매개변수 및 nonce 매개변수를 결합합니다. </tr> <tr class="even"> <td align="left"></td>timestamp<td align="left"></td>timestamp</tr> <tr class="odd"> <td align="left"></td>nonce<td align="left"></td>random number</tr> <tr class="even"> <td align="left"></td>echostr<td align="left"></td>random string</tr> </tbody> </table>🎜<p>微信服务器会通过GET请求,来请求我们所配置的接口,并带上以上表格的信息,而我们必须按照以下要求,将微信发送的信息进行要求校验,以确保是微信发送的信息,其中校验流程如下:</p> <blockquote><p>1)将token、timestamp、nonce三个参数进行字典序排序<br>2)将三个参数字符串拼接成一个字符串进行sha1加密<br>3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信</p></blockquote> <div class="sourceCode"><pre class="sourceCode javascript">const express = require(&amp;#39;express&amp;#39;)const app = express()const sha1 = require(&amp;#39;sha1&amp;#39;)app.get(&amp;#39;/wxJssdk&amp;#39;, (req, res) =&gt; { let wx = req.query let token = &amp;#39;jegfjaeghfuccawegfgjdbh&amp;#39; let timestamp = wx.timestamp let nonce = wx.nonce // 1)将token、timestamp、nonce三个参数进行字典序排序 let list = [token, timestamp, nonce].sort() // 2)将三个参数字符串拼接成一个字符串进行sha1加密 let str = list.join(&amp;#39;&amp;#39;) let result = sha1(str) // 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信 if (result === wx.signature) {res.send(wx.echostr) // 返回微信传来的echostr,表示校验成功,此处不能返回其它 } else {res.send(false) }})</pre></div> <p>此时我们重启Node服务器,再次保存接口配置信息即可配置成功。</p> <h2>微信JSSDK使用步骤</h2> <p>根据微信JSSDK说明文档,我们需要完成如下:</p> <h3>填写安全域名</h3> <p>登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”,即要调用接口的域名,不包含协议</p> <h3>前端引入JS</h3> <p>在需要调用JS接口的页面引入此JS文件,(支持https):</p> <h3>填写接口的配置信息</h3> <div class="sourceCode"><pre class="sourceCode javascript">wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: &amp;#39;&amp;#39;, // 必填,公众号的唯一标识 timestamp: , // 必填,生成签名的时间戳 nonceStr: &amp;#39;&amp;#39;, // 必填,生成签名的随机串 signature: &amp;#39;&amp;#39;,// 必填,签名 jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2});</pre></div> <h3>调用接口</h3> <p>做你前端该做的,调用微信分享接口,或微信提供的其它接口,whatever you need,当然,这并不是我们所要讲的重点,我们接下来要看一下微信的配置信息从哪获取</p> <h2>在Node服务器中生成jssdk所需要的配置信息</h2> <p>从上一节可以看到,调用微信JSSDK需要以下信息</p> <ol class=" list-paddingleft-2"> <li><p>appId</p></li> <li><p>timestamp</p></li> <li><p>nonceStr</p></li> <li><p>signature</p></li> <li><p>jsApiList</p></li> </ol> <p>其中:</p> <ol class=" list-paddingleft-2"> <li><p>第1项appId是测试公众号后台的appId,我们已知</p></li> <li><p>第2项时间戳我们也可以自己生成</p></li> <li><p>第3项nonceStr可以随意填写,你可以理解为密钥</p></li> <li><p>第4项signature则需要我们按要求生成</p></li> <li><p>第5项是所需要接口的接口名</p></li> </ol> <h3>生成signature</h3> <blockquote><p>生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。</p></blockquote> <p>为了保证我们appid,appsecret,nonceStr等信息不在前端曝露,我们以下步骤将在服务器上进行操作,以免他人盗用信息获取(注:微信请求有每日次数限制,一旦超出,则无法使用,具体请求次数限制在微信公众号后台中可查看)</p> <h4>生成access_token</h4> <p>根据微信开发文档[获取access_token文档说明],我们需要将微信测试公众号后台的appid和和appsecret以GET的请求方式向 发起请求获取token,请求成功后我们会获得下返回JSON转化的字符串</p> <div class="sourceCode"><pre class="sourceCode json">{&quot;access_token&quot;:&quot;ACCESS_TOKEN&quot;,&quot;expires_in&quot;:7200}</pre></div> <p>具体请求代码如下:</p> <div class="sourceCode"><pre class="sourceCode javascript">const request = require(&amp;#39;request&amp;#39;)const grant_type = &amp;#39;client_credential&amp;#39;const appid = &amp;#39;your app id&amp;#39;const secret = &amp;#39;your app secret&amp;#39;request(&amp;#39;https://api.weixin.qq.com/cgi-bin/token?grant_type=&amp;#39; + grant_type + &amp;#39;&amp;appid=&amp;#39; + appid + &amp;#39;&amp;secret=&amp;#39; + secret, (err, response, body) =&gt; { let access_toekn = JSON.parse(body).access_token})</pre></div> <h4>获取jsapi_ticket</h4> <div class="sourceCode"><pre class="sourceCode javascript">const request = require(&amp;#39;request&amp;#39;)const grant_type = &amp;#39;client_credential&amp;#39;const appid = &amp;#39;your app id&amp;#39;const secret = &amp;#39;your app secret&amp;#39;request(&amp;#39;https://api.weixin.qq.com/cgi-bin/token?grant_type=&amp;#39; + grant_type + &amp;#39;&amp;appid=&amp;#39; + appid + &amp;#39;&amp;secret=&amp;#39; + secret, (err, response, body) =&gt; { let access_toekn = JSON.parse(body).access_token request(&amp;#39;https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=&amp;#39; + access_token + &amp;#39;&amp;type=jsapi&amp;#39;, (err, response, body) =&gt; { let jsapi_ticket = JSON.parse(body).ticket })})</pre></div> <h4>生成签名</h4> <p>生成签名的步骤和最开始的<code>/wxJssdk的算法是一致的,具体如下:

let jsapi_ticket = jsapi_ticket  // 上一步从获取的jsapi_ticketlet nonce_str = &#39;123456&#39;    // 密钥,字符串任意,可以随机生成let timestamp = new Date().getTime()  // 时间戳let url = req.query.url   // 使用接口的url链接,不包含#后的内容// 将请求以上字符串,先按字典排序,再以&#39;&&#39;拼接,如下:其中j > n > t > u,此处直接手动排序let str = &#39;jsapi_ticket=&#39; + jsapi_ticket + &#39;&noncestr=&#39; + nonce_str + &#39;&timestamp=&#39; + timestamp + &#39;&url=&#39; + url// 用sha1加密let signature = sha1(str)

连接后的代码为:

const request = require(&#39;request&#39;)const grant_type = &#39;client_credential&#39;const appid = &#39;your app id&#39;const secret = &#39;your app secret&#39;request(&#39;https://api.weixin.qq.com/cgi-bin/token?grant_type=&#39; + grant_type + &#39;&appid=&#39; + appid + &#39;&secret=&#39; + secret, (err, response, body) => {
  let access_toekn = JSON.parse(body).access_token

  request(&#39;https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=&#39; + access_token + &#39;&type=jsapi&#39;, (err, response, body) => { let jsapi_ticket = JSON.parse(body).ticket let nonce_str = &#39;123456&#39;    // 密钥,字符串任意,可以随机生成 let timestamp = new Date().getTime()  // 时间戳 let url = req.query.url   // 使用接口的url链接,不包含#后的内容 // 将请求以上字符串,先按字典排序,再以&#39;&&#39;拼接,如下:其中j > n > t > u,此处直接手动排序 let str = &#39;jsapi_ticket=&#39; + jsapi_ticket + &#39;&noncestr=&#39; + nonce_str + &#39;&timestamp=&#39; + timestamp + &#39;&url=&#39; + url     // 用sha1加密 let signature = sha1(str)  })})

曝露接口,返回给前端

app.post(&#39;/wxJssdk/getJssdk&#39;, (req, res) => {
  const request = require(&#39;request&#39;)  const grant_type = &#39;client_credential&#39;
  const appid = &#39;your app id&#39;
  const secret = &#39;your app secret&#39;

  request(&#39;https://api.weixin.qq.com/cgi-bin/token?grant_type=&#39; + grant_type + &#39;&appid=&#39; + appid + &#39;&secret=&#39; + secret, (err, response, body) => {let access_toekn = JSON.parse(body).access_tokenrequest(&#39;https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=&#39; + access_token + &#39;&type=jsapi&#39;, (err, response, body) => {   let jsapi_ticket = JSON.parse(body).ticket   let nonce_str = &#39;123456&#39;    // 密钥,字符串任意,可以随机生成   let timestamp = new Date().getTime()  // 时间戳   let url = req.query.url   // 使用接口的url链接,不包含#后的内容   // 将请求以上字符串,先按字典排序,再以&#39;&&#39;拼接,如下:其中j > n > t > u,此处直接手动排序   let str = &#39;jsapi_ticket=&#39; + jsapi_ticket + &#39;&noncestr=&#39; + nonce_str + &#39;&timestamp=&#39; + timestamp + &#39;&url=&#39; + url       // 用sha1加密   let signature = sha1(str)       res.send({ appId: appid, timestamp: timpstamp, nonceStr: nonce_str, signature: signature,   })})  })})

前端请求后端接口,获取配置信息

获取配置

axios.post(&#39;/wxJssdk/getJssdk&#39;, {url: location.href}).then((response) => {
  var data = response.data

  wx.config({debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。appId: data.appId, // 必填,公众号的唯一标识timestamp: data.timestamp, // 必填,生成签名的时间戳nonceStr: data.nonceStr, // 必填,生成签名的随机串signature: data.signature,// 必填,签名,见附录1jsApiList: [&#39;onMenuShareTimeline&#39;, &#39;onMenuShareAppMessage&#39;] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
  });})

做你想做的,比如,自定义分享

if (wx) {
  axios.post(&#39;/wxJssdk/getJssdk&#39;, {url: location.href}).then((response) => {var data = response.datawx.config({  debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。  appId: data.appId, // 必填,公众号的唯一标识  timestamp: data.timestamp, // 必填,生成签名的时间戳  nonceStr: data.nonceStr, // 必填,生成签名的随机串  signature: data.signature,// 必填,签名,见附录1  jsApiList: [&#39;onMenuShareTimeline&#39;, &#39;onMenuShareAppMessage&#39;] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2});wx.ready(function () {  wx.onMenuShareTimeline({  title: wxShare.title,  desc: wxShare.desc,  link: wxShare.link,  imgUrl: wxShare.imgUrl  });  wx.onMenuShareAppMessage({  title: wxShare.title,  desc: wxShare.desc,  link: wxShare.link,  imgUrl: wxShare.imgUrl});
  })wx.error(function (res) {   // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。})  })}

至此,后端配置好了,我们已经能够正常使用微信的接口了,但是微信每日接口请求是有上限的,通过2000次/天,因此如果网站上线后,一量当天访问量超过2000次你的接口将失效,而且每次都请求微信接口两次,造成请求时间浪费,所以我们需要将以上获取信息缓存在后端,避免造成接口失效以及多次请求微信后台。

缓存access_token及jsapi_ticket

此处直接上代码,利用node_cache包进行缓存

const request = require(&#39;request&#39;)const express = require(&#39;express&#39;)const app = express()const sha1 = require(&#39;sha1&#39;)const waterfall = require(&#39;async/waterfall&#39;)const NodeCache = require(&#39;node-cache&#39;)const cache = new NodeCache({stdTTL: 3600, checkperiod: 3600}) //3600秒后过过期app.get(&#39;/wxJssdk&#39;, (req, res) => {
  let wx = req.query

  // 1)将token、timestamp、nonce三个参数进行字典序排序
  let token = &#39;jegfjaeghfuyawegfgjdbh&#39;
  let timestamp = wx.timestamp
  let nonce = wx.nonce

  // 2)将三个参数字符串拼接成一个字符串进行sha1加密
  let list = [token, timestamp, nonce]  let result = sha1(list.sort().join(&#39;&#39;))  // 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
  if (result === wx.signature) {res.send(wx.echostr)  } else {res.send(false)  }})app.get(&#39;/wxJssdk/getJssdk&#39;, (req, res) => {
  let grant_type = &#39;client_credential&#39;
  let appid = &#39;your app id&#39;
  let secret = &#39;your app secret&#39; // appscret

  let steps = []  // 第一步,获取access_token
  steps.push((cb) => {

  let steps1 = []// 第1.1步,从缓存中读取access_tokensteps1.push((cb1) => {  let access_token = cache.get(&#39;access_token&#39;, (err, access_token) => {cb1(err, access_token)      })})// 第1.2步,缓存中有access_token则直接返回,如果没有,则从服务器中读取access_tokensteps1.push((access_token, cb1) => {  if (access_token) {cb1(null, access_token, &#39;from_cache&#39;)      } else {request(&#39;https://api.weixin.qq.com/cgi-bin/token?grant_type=&#39; + grant_type + &#39;&appid=&#39; + appid + &#39;&secret=&#39; + secret, (err, response, body) => {  cb1(err, JSON.parse(body).access_token, &#39;from_server&#39;)})      }})// 第1.3步,如果是新从服务器取的access_token,则缓存起来,否则直接返回steps1.push((access_token, from_where, cb1) => {  if (from_where === &#39;from_cache&#39;) {console.log(&#39; === 成功从缓存中读取access_token: &#39; + access_token + &#39; ===&#39;)cb1(null, access_token)      } else if (from_where === &#39;from_server&#39;) {cache.set(&#39;access_token&#39;, access_token, (err, success) => {  if (!err && success) {console.log(&#39; === 缓存已过期,从服务器中读取access_token: &#39; + access_token + &#39; ===&#39;)cb1(null, access_token)          } else {cb1(err || &#39;cache设置access_token时,出现未知错误&#39;)          }})      } else {cb1(&#39;1.3获取from_where时,from_where值为空&#39;)      }})waterfall(steps1, (err, access_token) => {  cb(err, access_token)})  })  // 第二步,获取ticket
  steps.push((access_token, cb) => {let steps1 = []// 第2.1步,从缓存中读取ticketsteps1.push((cb1) => {  let ticket = cache.get(&#39;ticket&#39;, (err, ticket) => {cb1(err, ticket)      })})// 第2.2步,缓存中有ticket则直接返回,如果没有,则从服务器中读取ticketsteps1.push((ticket, cb1) => {  if (ticket) {cb1(null, ticket, &#39;from_cache&#39;)      } else {request(&#39;https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=&#39; + access_token + &#39;&type=jsapi&#39;, (err, response, body) => {  cb1(err, JSON.parse(body).ticket, &#39;from_server&#39;)})      }})// 第2.3步,如果新从服务器取的ticket,则缓存起来,否则直接返回steps1.push((ticket, from_where, cb1) => {  if (from_where === &#39;from_cache&#39;) {console.log(&#39; === 成功从缓存中读取ticket: &#39; + ticket + &#39; ===&#39;)cb1(null, ticket)      } else if (from_where === &#39;from_server&#39;) {cache.set(&#39;ticket&#39;, ticket, (err, success) => {  if (!err && success) {console.log(&#39; === 缓存已过期,从服务器中读取ticket: &#39; + ticket + &#39; ===&#39;);cb1(null, ticket)          } else {cb1(err || &#39;cache设置ticket时,出现未知错误&#39;)          }})      } else {cb1(&#39;2.3获取from_where时,from_where值为空&#39;)      }})waterfall(steps1, (err, ticket) => {  cb(err, ticket)})  })  // 第三步,生成签名
  steps.push((ticket, cb) => {let jsapi_ticket = ticketlet nonce_str = &#39;123456&#39;let timestamp = new Date().getTime()let url = req.query.urllet str = &#39;jsapi_ticket=&#39; + jsapi_ticket + &#39;&noncestr=&#39; + nonce_str + &#39;&timestamp=&#39; + timestamp + &#39;&url=&#39; + urllet signature = sha1(str)cb(null, {  appId: appid,  timestamp: timestamp,  nonceStr: nonce_str,  signature: signature,  ticket: ticket})  })  waterfall(steps, (err, data) => {if (err) {  res.send({status: &#39;error&#39;, data: err})} else {  res.send({status: &#39;success&#39;, data: data})}
  })})app.use(&#39;/wxJssdk/public&#39;, express.static(&#39;public&#39;))app.listen(80, err => {
  if(!err) console.log(&#39;connect succeed&#39;)})

위 내용은 WeChat JS-SDK 백엔드 인터페이스에서 Node.js 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.