先给猴急的客官上干货代码

GitHub

接入微信JSSDK GitHub地址

Gitee

接入微信JSSDK GitHub地址

前言

事件的起因是因为疫情重大,领导要求做一个专题页,可能尽可能帮忙所须要的人。
于是乎本狗与共事挑灯奋战,加班加点赶工进去。
部署上线实现,用微信内置浏览器分享后,现实状态应该是这样的,如下图⬇️


然而,后果却不是现实的这样,默默地留下了没有技术的泪水,如下图⬇️

居然没有关键字和展现图片,在本菜狗的不懈努力下,终于抵赖技术不行,去求教了大佬,得出如下论断。

  • 1.微信内置浏览器分享若须要自定义展现形容及右侧封面图,必须接入微信JSSDK,并且肯定须要有配合本站的微信公众号(appId和appSecret)才可自定义分享,切记小程序(appId和appSecret)的不能够
  • 2.非微信分享,如QQ浏览器,UC浏览器等各大厂商,会依据本身定义获取HTML页面中的TDK(title,description,keyword),举例UC浏览器分享⬇️


    所以,对接微信JSSDK,势在必行!


    Tip

    史上最具体的接入微信JSSDK菜鸟教程,本文全面的记录了接入微信JSSDK的步骤,具体的代码及遇到的坑,并且展现公布最终成果,并将代码公布GitHub。随篇幅较长,但史上最全。大佬勿喷,新手入门,亲测可用!!!

本文试用人群

  • 须要接入微信JSSDK却看不懂文档的同学
  • 看懂文档然而实操不知如何下手的同学
  • 下了手然而出错不晓得如何调试批改的同学
  • 胜利接入过然而想重温具体流程的同学

本文指标

  • 实战进行H5网站微信自定义分享
  • 实战进行H5网站调取相册选取图片

放松心态,缓缓来看


注释

官网文档

任何平台接入,官网文档是标杆,虽有些关键点一笔带过,咱们也要通读有个印象,点击微信官网文档打开文档,如下⬇️

总览
  • 1.x是接入关键步骤,需认真品读,与接入无关
  • 2.x - 12.x 具体接口接入,须要对接时具体参考
  • 13.x 须要留神下,api_ticket 微信长期票据,与接入无关
  • 16-22 均是附录,可查阅谬误列表对应含意,及接口菜单列表等形容


    实操步骤

    应用IDEA工具,新建SpringBoot我的项目,我的项目名为springboot-wexin,目录构造如下

    AjaxJson.java - 自定义接口返回前台数据格式的封装类

    /** * Copyright &copy; 2005-2020 <a href="http://www.jhmis.com/">jhmis</a> All rights reserved. */package net.javadog.springbootwexin.common;import com.fasterxml.jackson.annotation.JsonIgnore;import java.util.LinkedHashMap;import java.util.List;/** * $.ajax后须要承受的JSON * */public class AjaxJson {  private boolean success = true;// 是否胜利  private String errorCode = "-1";//错误代码  private String msg = "操作胜利";// 提示信息  private Long count;             //返回表格记录数量  private List<?> data;           //返回表格数据  private LinkedHashMap<String, Object> body = new LinkedHashMap<String, Object>();//封装json的map  public static AjaxJson ok(){      AjaxJson j = new AjaxJson();      return j;  }  public static AjaxJson ok(String msg){      AjaxJson j = new AjaxJson();      j.setMsg(msg);      return j;  }  public static AjaxJson ok(String msg, Object object){      AjaxJson j = new AjaxJson();      j.setMsg(msg);      j.setResult(object);      return j;  }  public static AjaxJson ok(Object object){      AjaxJson j = new AjaxJson();      j.setResult(object);      return j;  }  public static AjaxJson fail(String errorMsg){      AjaxJson j = new AjaxJson();      j.setSuccess(false);      j.setErrorCode("999");//默认错误码      j.setMsg(errorMsg);      return j;  }  public static AjaxJson fail(String errorCode,String errorMsg){      AjaxJson j = new AjaxJson();      j.setSuccess(false);      j.setErrorCode(errorCode);      j.setMsg(errorMsg);      return j;  }  //返回不分页的layui表数据  public static AjaxJson layuiTable(List<?> list){      AjaxJson j = new AjaxJson();      j.setSuccess(true);      j.setCount(Long.valueOf(list.size()));      j.setData(list);      return j;  }  public LinkedHashMap<String, Object> getBody() {      return body;  }  public void setBody(LinkedHashMap<String, Object> body) {      this.body = body;  }  public void put(String key, Object value){//向json中增加属性,在js中拜访,请调用data.map.key      body.put(key, value);  }    public void remove(String key){      body.remove(key);  }  /**   * 间接设置result内容   * @param result   */  public void setResult(Object result){      body.put("result", result);  }  @JsonIgnore//返回对象时疏忽此属性  public Object getResult(){      return body.get("result");  }  public String getMsg() {      return msg;  }  public void setMsg(String msg) {//向json中增加属性,在js中拜访,请调用data.msg      this.msg = msg;  }  public boolean isSuccess() {      return success;  }  public void setSuccess(boolean success) {      this.success = success;  }    public void setErrorCode(String errorCode) {      this.errorCode = errorCode;  }  public String getErrorCode() {      return errorCode;  }  public Long getCount() {      return count;  }  public void setCount(Long count) {      this.count = count;  }  public List<?> getData() {      return data;  }  public void setData(List<?> data) {      this.data = data;  }}

    WxInitController.java - 微信初始化接入Controller控制器

    package net.javadog.springbootwexin.controller;import net.javadog.springbootwexin.common.AjaxJson;import net.javadog.springbootwexin.service.WxService;import net.javadog.springbootwexin.utils.WxUtil;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.util.Map;/** * 一个低端大方没品位的程序狗 JavaDog * blog.javadog.net * * @BelongsProject: springboot-wexin * @BelongsPackage: net.javadog.springbootwexin.controller * @Author: hdx * @CreateTime: 2020-02-14 14:52 * @Description: 微信初始化接入Controller控制器 */@RestController@RequestMapping("/weixin")public class WxInitController {  protected Logger logger = LoggerFactory.getLogger(getClass());  @Autowired  private WxService wxService;  /**  *@Author: hdx  *@CreateTime: 20:39 2020/2/14  *@param:  shareUrl 分享url地址  *@Description: 初始化微信JSSDK Config信息   1.先通过appId和appSecret参数申请指定微信地址 获取AccessToken   2.在通过第一步中的AccessToken作为参数申请微信地址 获取jsapi_ticket长期票据(此处不思考调用频率,使用者依据状况放入缓存或定时工作)   3.通过第二步的JssdkGetticket和timestamp,nonceStr,url作为参数申请微信地址,获取签名signature   4.将第三步取得的signature和jsapi_ticket,nonceStr,timestamp,url返回给前端,作为Config初始化验证的信息  */  @RequestMapping("/initWXJSSDKConfigInfo")  public AjaxJson initWXJSConfig (@RequestParam(required = false) String url) throws Exception{      logger.info("url=" + url);      String json = "";      try {          Map map = wxService.initJSSDKConfig(url);          json = WxUtil.mapToJson(map);      }catch (Exception e){          AjaxJson.fail(e.getMessage());      }      return AjaxJson.ok(json);  }}

    WxService.java - 初始化JSSDKConfig

    package net.javadog.springbootwexin.service;import lombok.Getter;import net.javadog.springbootwexin.utils.WxUtil;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import java.util.HashMap;import java.util.Map;import java.util.UUID;/** * 一个低端大方没品位的程序狗 JavaDog * blog.javadog.net * * @BelongsProject: springboot-wexin * @BelongsPackage: net.javadog.springbootwexin.service * @Author: hdx * @CreateTime: 2020-02-14 20:43 * @Description: 微信相干service */@Servicepublic class WxService {  @Getter  private static String AppId;  @Value("${wx.appId}")  public void setAppId(String appId) {      AppId = appId;  }  /**  *@Author: hdx  *@CreateTime: 20:46 2020/2/14  *@param:  shareUrl 分享的url  *@Description: 初始化JSSDKConfig  */  public Map initJSSDKConfig(String url) throws Exception {      //获取AccessToken      String accessToken = WxUtil.getJSSDKAccessToken();      //获取JssdkGetticket      String jsapiTicket = WxUtil.getJssdkGetticket(accessToken);      String timestamp = Long.toString(System.currentTimeMillis() / 1000);      String nonceStr = UUID.randomUUID().toString();      String signature = WxUtil.buildJSSDKSignature(jsapiTicket,timestamp,nonceStr,url);      Map<String,String> map = new HashMap<String,String>();      map.put("url", url);      map.put("jsapi_ticket", jsapiTicket);      map.put("nonceStr", nonceStr);      map.put("timestamp", timestamp);      map.put("signature", signature);      map.put("appid", AppId);      return map;  }}

    WxUtil.java - 微信工具类

    package net.javadog.springbootwexin.utils;import com.google.gson.Gson;import com.google.gson.reflect.TypeToken;import lombok.Getter;import org.springframework.beans.factory.annotation.Value;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Component;import org.springframework.web.client.RestTemplate;import java.security.MessageDigest;import java.util.Map;/** * 一个低端大方没品位的程序狗 JavaDog * blog.javadog.net * * @BelongsProject: springboot-wexin * @BelongsPackage: net.javadog.springbootwexin.utils * @Author: hdx * @CreateTime: 2020-02-14 21:19 * @Description: 微信工具类 */@Componentpublic class WxUtil {  @Getter  protected static String AppId;  @Getter  protected static String AppSecret;  @Getter  protected static String JssdkAccesstokenUrl;  @Getter  protected static String JssdkGetticketUrl;  @Value("${wx.appId}")  public void setAppId(String appId) {      AppId = appId;  }  @Value("${wx.appSecret}")  public void setAppSecret(String appSecret) {      AppSecret = appSecret;  }  @Value("${wx.jssdk_accesstoken_url}")  public void setJssdkAccesstokenUrl(String jssdkAccesstokenUrl) {      JssdkAccesstokenUrl = jssdkAccesstokenUrl;  }  @Value("${wx.jssdk_getticket_url}")  public void setJssdkGetticketUrl(String jssdkGetticketUrl) {      JssdkGetticketUrl = jssdkGetticketUrl;  }  /**  *@Author: hdx  *@CreateTime: 21:31 2020/2/14  *@param:  * @param null  *@Description:    */  public static String getJSSDKAccessToken() {      String token = null;      String url = JssdkAccesstokenUrl.replaceAll("APPID",              AppId).replaceAll("APPSECRET",              AppSecret);      String json = postRequestForWeiXinService(url);      Map map = jsonToMap(json);      if (map != null) {          token = (String) map.get("access_token");      }      return token;  }  /**  *@Author: hdx  *@CreateTime: 21:40 2020/2/14  *@param:  * @param null  *@Description: 依据accessToken获取JssdkGetticket  */  public static String getJssdkGetticket(String accessToken) {      String url = JssdkGetticketUrl.replaceAll("ACCESS_TOKEN", accessToken);      String json = postRequestForWeiXinService(url);      Map map = jsonToMap(json);      String jsapi_ticket = null;      if (map != null) {          jsapi_ticket = (String) map.get("ticket");      }      return jsapi_ticket;  }  /**  *@Author: hdx  *@CreateTime: 21:41 2020/2/14  *@param:ticket 依据accessToken生成的JssdkGetticket  *@param:timestamp 领取签名工夫戳,留神微信jssdk中的所有应用timestamp字段均为小写。但最新版的领取后盾生成签名应用的timeStamp字段名需大写其中的S字符  *@param:nonceStr 随机字符串  *@param:url 以后网页的URL  *@Description: 构建分享链接的签名  */  public static String buildJSSDKSignature(String ticket,String timestamp,String nonceStr ,String url) throws Exception {      String orderedString = "jsapi_ticket=" + ticket              + "&noncestr=" + nonceStr + "&timestamp=" + timestamp              + "&url=" + url;      return sha1(orderedString);  }  /**   * sha1 加密JSSDK微信配置参数获取签名。   *   * @return   */  public static String sha1(String orderedString) throws Exception {      String ciphertext = null;      MessageDigest md = MessageDigest.getInstance("SHA-1");      byte[] digest = md.digest(orderedString.getBytes());      ciphertext = byteToStr(digest);      return ciphertext.toLowerCase();  }  /**   * 将字节数组转换为十六进制字符串   *   * @param byteArray   * @return   */  private static String byteToStr(byte[] byteArray) {      String strDigest = "";      for (int i = 0; i < byteArray.length; i++) {          strDigest += byteToHexStr(byteArray[i]);      }      return strDigest;  }  /**   * 将字节转换为十六进制字符串   *   * @param mByte   * @return   */  private static String byteToHexStr(byte mByte) {      char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };      char[] tempArr = new char[2];      tempArr[0] = Digit[(mByte >>> 4) & 0X0F];      tempArr[1] = Digit[mByte & 0X0F];      String s = new String(tempArr);      return s;  }  /**  *@Author: hdx  *@CreateTime: 21:49 2020/2/14  *@param:  map  *@Description:  mapToJson  */  public static String mapToJson(Map map){      Gson gson = new Gson();      String json = gson.toJson(map);      return json;  }  /**  *@Author: hdx  *@CreateTime: 21:37 2020/2/14  *@param:  json  *@Description: jsonToMap  */  private static Map jsonToMap(String json) {      Gson gons = new Gson();      Map map = gons.fromJson(json, new TypeToken<Map>(){}.getType());      return map;  }  /**  *@Author: hdx  *@CreateTime: 21:36 2020/2/14  *@param:  * @param null  *@Description: 调取微信接口  */  private static String postRequestForWeiXinService(String getAccessTokenUrl) {      RestTemplate restTemplate = new RestTemplate();      ResponseEntity<String> postForEntity = restTemplate.postForEntity(getAccessTokenUrl, null, String.class);      String json = postForEntity.getBody();      return json;  }}

    SpringbootWexinApplication.java - SpringBoot启动类

    package net.javadog.springbootwexin;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class SpringbootWexinApplication {  public static void main(String[] args) {      SpringApplication.run(SpringbootWexinApplication.class, args);  }}

    config/application.yml - 根底配置文件

    spring:profiles:  #激活配置文件  active: prod#配置动态资源门路resources:  static-locations: classpath:/static/#日志相干logging:#配置文件日志门路config: classpath:logback-spring.xml#微信相干配置wx:#appId (到时候换成本人公众号的)appId: wx4ad618620f8c3528#appSecret(到时候换成本人公众号的)appSecret: b772c7863b29e270aa86e40f9b9e6215#参考以下文档获取access_token(有效期7200秒,开发者必须在本人的服务全局缓存access_token)jssdk_accesstoken_url: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET#用第一步拿到的access_token 采纳http GET形式申请取得jsapi_ticket(有效期7200秒,开发者必须在本人的服务全局缓存jsapi_ticket)jssdk_getticket_url: https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

    application-dev.yml -开发配置文件(可选)

    # 开发环境配置spring:profiles: dev#端口设置server:port: 8000

application-prod.yml -生产配置文件(因JS接口平安域名限度,则采取正式生产配置)

# 生产环境配置spring:  profiles: prod#端口设置server:  port: 8002

application-test.yml -测试配置文件(可选)

# 生产环境配置spring:  profiles: prod#端口设置server:  port: 8002

demo.html - 测试h5页面

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>测试jssdk</title>    <!--引入微信JS文件-->    <script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js" type="text/javascript"></script>    <!--引入jquery-->    <script src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js"></script><script>    //获取以后页面地址    var url = (window.location.href).split('#')[0];    //调取后盾接口获取权限验证配置    $.ajax({        type : "get",        /*!!!切记到时候改成本人的*/        url : "http://wxjssdk.javadog.net/weixin/initWXJSSDKConfigInfo?url="+url,//替换网址,xxx依据本人jssdk文件地位批改        success : function(data){            console.log("返回值为=" + data);            var msg = "";            if(data.success){                msg = JSON.parse(data.msg);            }            //通过config接口注入权限验证配置            wx.config({                debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert进去,若要查看传入的参数,能够在pc端关上,参数信息会通过log打出,仅在pc端时才会打印                appId: msg.appid,                timestamp: msg.timestamp,                nonceStr: msg.nonceStr,                signature: msg.signature,                /*!!!切记到时候按需本人抉择,参考文档填写*/                jsApiList: [                    "onMenuShareAppMessage",//分享给好友                    "chooseImage"                ]            });        },        error:function(data){            alert(JSON.stringify(data));        }    });    //通过ready接口解决胜利验证    wx.ready(function (){        wx.checkJsApi({            jsApiList: ['chooseImage','onMenuShareAppMessage'],            success: function (res) {JSON.stringify(res)}        });        var shareData = {            title: '题目',            desc: '简介',//这里请特地留神是要去除html            link: url,            imgUrl: 'http://b2b.haier.com/shop/userfiles/sys/1/files/201912/af656b3a-8c2c-424d-937b-a8035deb78f5.jpg'        };        wx.onMenuShareAppMessage(shareData);    });    //从相册选取图片    function wxchooseImage(){        wx.chooseImage({            count: 1, // 默认9            sizeType: ['original', 'compressed'], // 能够指定是原图还是压缩图,默认二者都有            sourceType: ['album', 'camera'], // 能够指定起源是相册还是相机,默认二者都有            success: function (res) {                var localIds = res.localIds; // 返回选定照片的本地ID列表,localId能够作为img标签的src属性显示图片            }        });    }</script></head><body><button onclick="wxchooseImage();">点我选取相册</button></body></html>

nginx 配置,此处不是我的项目中的代码!!!
nginx.config - nginx服务器配置

 server    {        listen   80;                           #监听端口设为 80。        server_name  wxjssdk.javadog.net;      #请绑定本人的前缀域名。        location / {        proxy_set_header HOST $host;        proxy_set_header X-Forwarded-Proto $scheme;        proxy_set_header X-Real-IP $remote_addr;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_pass http://127.0.0.1:8002/;      }    }

MP_verify_B0vMQLCguxRzP1Rc.txt - JS接口平安域名验证文件(到时候替换成本人公众号上的),必须在域名根门路下能够拜访

#肯定把本人公众号上的txt验证文件放上!!!B0vMQLCguxRzP1Rc

步骤详解

打开文档JSSDK应用步骤段落,如下⬇️

1.绑定域名


先登录微信公众平台进入“公众号设置”的“性能设置”里填写“JS接口平安域名”。如下⬇️

点击设置如下⬇️

关键点
  • 1.只能是三个域名或门路,中文,ip,带端口等门路均不可
  • 2.域名必须是ICP备案过的,有些同学应用内网穿透花生壳之类的无奈设置JS平安域名
  • 3.必须将txt文件搁置平安域名所对应的目录,如wxjssdk.javadog.net/xxx.txt。可由nginx配置,只有能拜访即可,如果拜访不到则无奈设置JS平安域名
2.引入JS文件


理论援用在咱们的我的项目Demo.html页面中第9行,如

3.通过config接口注入权限验证配置

关键点

必须在后盾凋谢一个对外获取config接口注入权限的接口

对应咱们代码中**WxInitController.java 中的initWXJSSDKConfigInfo()**办法,会返回文档中所需的必填项**appId,timestamp,nonceStr,signature**验证参数![在这里插入图片形容](https://img-blog.csdnimg.cn/20200224092342176.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JhaWR1XzI1OTg2MDU5,size_16,color_FFFFFF,t_70)
实现步骤
 1.先通过appId和appSecret参数申请指定微信地址 获取AccessToken 2.在通过第一步中的AccessToken作为参数申请微信地址 获取jsapi_ticket长期票据(此处不思考调用频率,使用者依据状况放入缓存或定时工作) 3.通过第二步的JssdkGetticket和timestamp,nonceStr,url作为参数申请微信地址,获取签名signature 4.将第三步取得的signature和jsapi_ticket,nonceStr,timestamp,url返回给前端,作为Config初始化验证的信息

  • 1.先通过appId和appSecret参数申请指定微信地址 获取AccessToken
    对应咱们代码中WxUtil.java第61行getJSSDKAccessToken()办法

    通过本人公众号的appId和appSecret调用微信服务器access_token接口地址获取token,地址如下
    https://api.weixin.qq.com/cgi-bin/token?grant_type=client_cre...
    将其中APPID和APPSECRET替换成本人公众号的appId和appSecret,调取后返回Json字符串后果,获取access_token
  • 2.通过第一步中的AccessToken作为参数申请微信地址 获取jsapi_ticket长期票据
    对应咱们代码中WxUtil.java第81行getJssdkGetticket()办法

    通过上一步取得的access_token调用微信服务器jsapi_ticket接口地址获取jsapi_ticket,地址如下
    https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_tok...
    将其中ACCESS_TOKEN替换成上一步获取的access_token,调取后返回Json字符串后果,获取jsapi_ticket
  • 3.通过第二步的JssdkGetticket和timestamp,nonceStr,url作为参数申请微信地址,获取签名signature
    对应咱们代码中WxUtil.java第102行buildJSSDKSignature()办法

    通过上一步取得的jsapi_ticket,加上timestamp(领取签名工夫戳),nonceStr(随机字符串),url(以后网页的URL),依照字段名的ASCII 码从小到大排序(字典序)后通过sha1进行签名,生成最终签名数据。
    对应咱们代码中WxUtil.java第115行sha1()办法
  • 4.前端胜利接到返回值

对应咱们代码中demo.html第16行$.ajax办法

接口返回值为

JSON.parse(msg)转化一下JSON对象,对应咱们代码中的Demo.html中24行,转化后数据做wx.config接口注入权限验证,对应咱们代码demo.html第37行

4.通过ready接口解决胜利验证

关键点

所以如果须要在页面加载时就调用相干接口,则须把相干接口放在ready函数中调用来确保正确执行
在咱们代码在demo.html第46行,自定义分享接口,须要在页面初始化加载时就放入ready才可失效

反之不须要初始化加载的即可通过用户事件触发执行即可
在咱们代码在demo.html第63行,用户点击按钮触发-拍照或从手机相册当选图接口

公布

我采纳的IDEA插件Alibaba Cloud Toolkit工具一键部署本地利用到ECS服务器,可百度或等我下篇文章详解一下这个插件的用法。


Target ECS:指标服务器,我买的是阿里的服务器,则间接能够搜寻到。
Target Directory: 目标目录,须要把打成的jar包上传至哪个门路下 如:/usr/local/hdx/web/
Command: 上传后执行的操作命令 如:nohup java -jar /usr/local/hdx/web/springboot-wexin.jar

公布胜利后会在终端呈现胜利提示信息

而后功败垂成,拜访一下试试 http://wxjssdk.javadog.net/demo.html
如果调试举荐应用微信开发者工具,也就是

切记配置nginx服务器平安端口拜访权限!!!否则会404哦!!!

测试

  • 1.先来测试下拍照或从手机相册当选图接口

    调试失常
  • 2.再来测试微信内置分享

    调试报错,这是个小坑。本狗在这调试了良久,起因出在集体的订阅号是没有自定义分享权限的!!

小坑总结

  1. 订阅号和服务号所波及权限不同,需具体查看微信开发文档,查问公众号权限
  2. IP白名单未设置,会报40164

    IP白名单须要在公众号平安核心设置
  3. invalid signature 签名异样
    倡议应用微信JSSDK签名验证工具验证是否有误

我是JavaDog,谢谢博友急躁看完, 抽空来我狗窝瞅瞅呗 blog.javadog.net