记录--微信调用jssdk全流程详解

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

微信调用jssdk全流程详解

系统框架使用的是前后端分离,前端使用vant,后端是springboot

一、网页授权的时序图

二、公众号配置

1. 绑定域名

登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。也就是这样:

点击设置之后,弹出这样一个输入框,输入服务器所在的域名:

2:引入js文件

直接在你的页面里引入js文件就行

1
<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>

三、前端方法

1. 初始化方法,从后台获取基本的参数

前端进入需要扫一扫功能的页面时候,在mounted方法里面,执行微信配置getWxConfig(),此方法主要是获取jssdk所需要的参数,先检查本地缓存的是否过期,过期则请求后台获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
export function getWxConfig() {
  //判断signature是否过期
  if (expireSign()) {
    let data = {};
    data.appId = localStorage.getItem('appId');
    data.timestamp = localStorage.getItem('timestamp');
    data.nonceStr = localStorage.getItem('nonceStr');
    data.signature = localStorage.getItem('signature');
    setWxConfig(data);
  } else {
    //如果过期了,请求后台获取
    let url = location.href.split('#')[0]; //获取锚点之前的链接,防止出现invalid signature错误
    wxSign({ url: url })
      .then(res => {
        console.log(res);
        if (res.code == 200) {
          localStorage.setItem('appId', res.data.appId);
          localStorage.setItem('timestamp', res.data.timestamp);
          localStorage.setItem('nonceStr', res.data.nonceStr);
          localStorage.setItem('signature', res.data.signature);
          localStorage.setItem('expireSignTime', res.data.expireTime);
          setWxConfig(res.data);
        } else {
          localStorage.removeItem('expireSignTime');
          Toast.fail('网络故障,请退出重新加载页面');
        }
      })
      .catch(error => {
        localStorage.removeItem('expireSignTime');
        Toast.fail('网络故障,请退出重新加载页面');
      });
  }
}

2:注入config配置

上面获取到后台的参数后,在页面使用wx.config接口注入权限验证配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function setWxConfig(data) {
  console.log(data);
  wx.config({
    debug: false, // true是开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: data.appId, // 必填,公众号的唯一标识
    timestamp: data.timestamp, // 必填,签名的时间戳,后台生成的
    nonceStr: data.nonceStr, // 必填,签名的随机串,后台生成的
    signature: data.signature, // 必填,签名,后台生成的
    jsApiList: ['scanQRCode'] // 必填,需要使用的JS接口列表,scanQRCode是调用扫一扫二维码
  });
  wx.error(function(res) {
    localStorage.removeItem('expireSignTime');
    Toast.fail('网络故障,请退出重新页面');
  });
}

3. 方法调用

config如果不报错,则在 wx.ready里面调用jssdk的方法

1
2
3
4
5
6
7
8
9
10
11
wx.ready(function () {
    wx.scanQRCode({
      needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果
      success: function (res) {
        let data = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
        let code = codeFormat(data);
        that.pointCode = code;
        that.saveSinglePoint();
      },
    });
  });

四、后端方法

1. 获取access_token和jsapi_Ticket,缓存使用了文件的方式,也支持redis等方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
 * 获取access_token和jsapi_ticket
 **/
public AppWechatEntity getJsapiTicket() {
    //logger.debug("--------------开始执行getJsapiTicket方法--------------");
    //定义过期时间
    AppWechatEntity appWechatEntity = new AppWechatEntity();
    String accessTokenString = "";
    String jsapiTicketString = "";
    String jsapi_ticket = "";
    String access_token = "";
    jsapiTicketString = readWechatTokenFile(getJsapiTicketFilePath());
    if (!StringUtils.isEmpty(jsapiTicketString)) {
        appWechatEntity = JSONObject.parseObject(jsapiTicketString, AppWechatEntity.class);
        long expireTime = appWechatEntity.getExpire_time();
        long curTime = create_timestamp();
        if (expireTime >= curTime && StrUtil.isNotEmpty(appWechatEntity.getJsapi_ticket())) {
            //logger.debug("已有的jsapi_ticket=" + jsapi_ticket);
            return appWechatEntity;
        }
    }
 
    long timestamp = create_timestamp() + 7000;//过期时间是2小时(7200s)
    access_token =
            getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + configBeanValue.appid + "&secret=" + configBeanValue.secret);
    Map accessTokenMap = new HashMap();
    accessTokenMap.put("expire_time", timestamp);
    accessTokenMap.put("access_token", access_token);
    accessTokenString = JSONObject.toJSONString(accessTokenMap);
 
    jsapi_ticket = getJsapiTicketData("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi");
    Map jsapiTicketMap = new HashMap();
    jsapiTicketMap.put("expire_time", timestamp);
    jsapiTicketMap.put("jsapi_ticket", jsapi_ticket);
    jsapiTicketString = JSONObject.toJSONString(jsapiTicketMap);
 
    // 写文件
    try {
        FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8);
        FileUtils.writeStringToFile(new File(getJsapiTicketFilePath()), jsapiTicketString, CharsetUtil.CHARSET_UTF_8);
        //logger.debug("写入文件成功");
    } catch (IOException e) {
        log.debug("写文件异常:" + e.getMessage());
        e.printStackTrace();
    }
 
    appWechatEntity.setJsapi_ticket(jsapi_ticket);
    appWechatEntity.setExpire_time(timestamp);
    appWechatEntity.setAccess_token(access_token);
    //logger.debug("--------------结束执行getJsapiTicket方法--------------");
    return appWechatEntity;
}

2.根据jsapi_ticket获取signature

上面获取了jsapi_ticket之后,使用jsapi_ticket,noncestr,timestamp和前端传入的url组装成签名字符串,使用sha1进行加密,返回到前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * 生成signature
 **/
@Override
public AppWechatEntity sign(String url) {
    Map<String, String> ret = new HashMap();
    String nonceStr = create_nonce_str();
    String timestamp = Long.toString(create_timestamp());
    String string1;
    String signature = "";
    //获取jsapi_ticket和过期时间
    AppWechatEntity appWechatEntity = getJsapiTicket();
    String jsapiTicket = appWechatEntity.getJsapi_ticket();
    Long expireTime = appWechatEntity.getExpire_time();
    //注意这里参数名必须全部小写,且必须有序
    string1 = "jsapi_ticket=" + jsapiTicket +
            "&noncestr=" + nonceStr +
            "&timestamp=" + timestamp +
            "&url=" + url;
    try {
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(string1.getBytes(StandardCharsets.UTF_8));
        signature = byteToHex(crypt.digest());
    } catch (Exception e) {
        e.printStackTrace();
    }
 
    appWechatEntity.setAppId(configBeanValue.appid);
    appWechatEntity.setExpire_time(expireTime);
    appWechatEntity.setNonceStr(nonceStr);
    appWechatEntity.setTimestamp(timestamp);
    appWechatEntity.setSignature(signature);
    return appWechatEntity;
}

3. 后端全部的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/**
  * 生成signature
  **/
 @Override
 public AppWechatEntity sign(String url) {
     Map<String, String> ret = new HashMap();
     String nonceStr = create_nonce_str();
     String timestamp = Long.toString(create_timestamp());
     String string1;
     String signature = "";
     //获取jsapi_ticket和过期时间
     AppWechatEntity appWechatEntity = getJsapiTicket();
     String jsapiTicket = appWechatEntity.getJsapi_ticket();
     Long expireTime = appWechatEntity.getExpire_time();
     //注意这里参数名必须全部小写,且必须有序
     string1 = "jsapi_ticket=" + jsapiTicket +
             "&noncestr=" + nonceStr +
             "&timestamp=" + timestamp +
             "&url=" + url;
     try {
         MessageDigest crypt = MessageDigest.getInstance("SHA-1");
         crypt.reset();
         crypt.update(string1.getBytes(StandardCharsets.UTF_8));
         signature = byteToHex(crypt.digest());
     } catch (Exception e) {
         e.printStackTrace();
     }
 
     appWechatEntity.setAppId(configBeanValue.appid);
     appWechatEntity.setExpire_time(expireTime);
     appWechatEntity.setNonceStr(nonceStr);
     appWechatEntity.setTimestamp(timestamp);
     appWechatEntity.setSignature(signature);
     return appWechatEntity;
 }
 
 
 /**
  * 获取jsapi_ticket
  **/
 public AppWechatEntity getJsapiTicket() {
     //logger.debug("--------------开始执行getJsapiTicket方法--------------");
     //定义过期时间
     AppWechatEntity appWechatEntity = new AppWechatEntity();
     String accessTokenString = "";
     String jsapiTicketString = "";
     String jsapi_ticket = "";
     String access_token = "";
     jsapiTicketString = readWechatTokenFile(getJsapiTicketFilePath());
     if (!StringUtils.isEmpty(jsapiTicketString)) {
         appWechatEntity = JSONObject.parseObject(jsapiTicketString, AppWechatEntity.class);
         long expireTime = appWechatEntity.getExpire_time();
         long curTime = create_timestamp();
         if (expireTime >= curTime && StrUtil.isNotEmpty(appWechatEntity.getJsapi_ticket())) {
             //logger.debug("已有的jsapi_ticket=" + jsapi_ticket);
             return appWechatEntity;
         }
     }
 
     long timestamp = create_timestamp() + 7000;//过期时间是2小时(7200s)
     access_token =
             getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + configBeanValue.appid + "&secret=" + configBeanValue.secret);
     Map accessTokenMap = new HashMap();
     accessTokenMap.put("expire_time", timestamp);
     accessTokenMap.put("access_token", access_token);
     accessTokenString = JSONObject.toJSONString(accessTokenMap);
 
     jsapi_ticket = getJsapiTicketData("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi");
     Map jsapiTicketMap = new HashMap();
     jsapiTicketMap.put("expire_time", timestamp);
     jsapiTicketMap.put("jsapi_ticket", jsapi_ticket);
     jsapiTicketString = JSONObject.toJSONString(jsapiTicketMap);
 
     // 写文件
     try {
         FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8);
         FileUtils.writeStringToFile(new File(getJsapiTicketFilePath()), jsapiTicketString, CharsetUtil.CHARSET_UTF_8);
         //logger.debug("写入文件成功");
     } catch (IOException e) {
         log.debug("写文件异常:" + e.getMessage());
         e.printStackTrace();
     }
 
     appWechatEntity.setJsapi_ticket(jsapi_ticket);
     appWechatEntity.setExpire_time(timestamp);
     appWechatEntity.setAccess_token(access_token);
     //logger.debug("--------------结束执行getJsapiTicket方法--------------");
     return appWechatEntity;
 }
 
 public String getAccessToken() {
     //logger.debug("--------------开始执行getAccessToken方法--------------");
     String access_token = "";
     String accessTokenString = "";
     AppWechatEntity appWechatEntity = new AppWechatEntity();
     accessTokenString = readWechatTokenFile(getAccessTokenFilePath());
     if (StringUtils.isNotEmpty(accessTokenString)) {
         appWechatEntity = JSONObject.parseObject(accessTokenString, AppWechatEntity.class);
         access_token = appWechatEntity.getAccess_token();
         long expireTime = appWechatEntity.getExpire_time();
         long curTime = create_timestamp();
         if (expireTime >= curTime) {
             //logger.debug("已有的access_token=" + access_token);
             return access_token;
         }
     }
 
     long timestamp = create_timestamp() + 6000;//过期时间是2小时,但是可以提前进行更新,防止前端正好过期
     access_token =
             getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + configBeanValue.appid + "&secret=" + configBeanValue.secret);
     Map accessTokenMap = new HashMap();
     accessTokenMap.put("expire_time", timestamp);
     accessTokenMap.put("access_token", access_token);
     accessTokenString = JSONObject.toJSONString(accessTokenMap);
 
     // 写文件
     try {
         FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8);
         //logger.debug("写入文件成功");
     } catch (IOException e) {
         log.debug("写文件异常:" + e.getMessage());
         e.printStackTrace();
     }
     //logger.debug("新的access_token=" + access_token);
     //logger.debug("--------------结束执行getAccessToken方法--------------");
     return access_token;
 }
 
 private String readWechatTokenFile(String filePath) {
     String content = "";
     try {
         if (new File(filePath).exists()) {
             FileReader fileReader = new FileReader(filePath, CharsetUtil.CHARSET_UTF_8);
             content = fileReader.readString();
         } else {
             new File(filePath).createNewFile();
         }
     } catch (IOException e) {
         log.error("读文件异常:" + e.getMessage());
         e.printStackTrace();
     }
     return content;
 }
 
 private String getAccessTokenData(String url) {
     String str = "";
     String result = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8);
     if (StringUtils.isBlank(result))
         return str;
     str = parseData("access_token", "expires_in", result);
     return str;
 }
 
 private String getJsapiTicketData(String url) {
     String str = "";
     String result = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8);
     if (StringUtils.isBlank(result))
         return str;
     str = parseData("ticket", "expires_in", result);
     return str;
 }
 
 private String parseData(String tokenName, String expiresInName, String data) {
     String tokenConent = "";
     JSONObject jsonObject = JSONObject.parseObject(data);
     try {
         tokenConent = jsonObject.get(tokenName).toString();
         if (StringUtils.isEmpty(tokenConent)) {
             log.error("token获取失败,获取结果" + data);
             return tokenConent;
         }
     } catch (Exception e) {
         log.error("token 结果解析失败,token参数名称: " + tokenName + "有效期参数名称:" + expiresInName + "token请求结果:" + data);
         e.printStackTrace();
         return tokenConent;
     }
     return tokenConent;
 }
 
 private String byteToHex(final byte[] hash) {
     Formatter formatter = new Formatter();
     for (byte b : hash) {
         formatter.format("%02x", b);
     }
     String result = formatter.toString();
     formatter.close();
     return result;
 }
 
 private String create_nonce_str() {
     return IdUtil.simpleUUID();
 }
 
 private Long create_timestamp() {
     return System.currentTimeMillis() / 1000;
 }
 
 
 private String getJsapiTicketFilePath() {
     return configBeanValue.tokenpath + "/" + configBeanValue.appid + "_jsapiTicket.txt";
 }
 
 private String getAccessTokenFilePath() {
     return configBeanValue.tokenpath + "/" + configBeanValue.appid + "_accessToken.txt";
 }

五.vue weixin-js-sdk进行微信分享

第一步:安装weixin-js-sdk   

1
npm install weixin-js-sdk

第二步:

在assets文件下新建个common文件夹 ,然后再新建个utils.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import wx from "weixin-js-sdk";
  
/*
* 微信分享
* 获取微信加签信息
* @param{data}:获取的微信加签
* @param{shareData}:分享配置参数
*/
export const wexinShare = (data, shareData) => {
    let appId = data.appId;
    let timestamp = data.timestamp;
    let nonceStr = data.nonceStr;
    let signature = data.signature;
    wx.config({
        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: appId, // 必填,公众号的唯一标识
        timestamp: timestamp, // 必填,生成签名的时间戳
        nonceStr: nonceStr, // 必填,生成签名的随机串
        signature: signature, // 必填,签名,见附录1
        jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    });
    wx.ready(function () {
        //分享到朋友圈”及“分享到QQ空间”
        wx.updateTimelineShareData({
            title: shareData.title, // 分享标题
            link: shareData.link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: shareData.imgUrl, // 分享图标
            success: function (res) {
                // 设置成功
                // console.log("分享成功返回的信息为:", res);
            }
        })
  
        //“分享给朋友”及“分享到QQ”
        wx.updateAppMessageShareData({
            title: shareData.title, // 分享标题
            desc: shareData.desc, // 分享描述
            link: shareData.link, // 分享链接
            imgUrl: shareData.imgUrl, // 分享图标
            success: function (res) {
                // console.log("分享成功返回的信息为:", res);;
            }
        })
  
    });
    wx.error(function (res) {
        // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
        console.log('验证失败返回的信息:', res);
    });
}

 第三步:调用

1
import {wexinShare} from "@/common/utils.js";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//请求微信配置参数接口(获取签名),由后台给接口给
get_share(){
    var that = this;
    var urls = that.$qs.stringify(window.location.href.split('#')[0]);
     
    //看后台请求接口是get/post
    that.$axios.post("/xxx",urls).then((res) => {
       if (res.data) {
        //微信加签
        var obj = {
            appId: res.data.appId,
            nonceStr: res.data.nonceStr,
            signature: res.data.signature,
            timestamp: res.data.timestamp
        }
        //分享数据,这段主要是为了在hash模式下分享出去的链接不被浏览器截取,保证完全把链接分享出去
        var shareWxLink = window.location.href.split('#')[0] + 'static/redirect.html?redirect=' + encodeURIComponent(window.location.href) + '&pid_uid=' + userinfo.uid;
        // console.log('shareWxLink', shareWxLink)
        let shareData = {
            title: '标题', // 分享标题
            link: shareWxLink,
            imgUrl: 'http://xxx.jpg', // 分享图标
            desc: '这里填写简介文字'
        }
        //引用
        wexinShare(obj, that, shareData);
        } else {
            that.$toast('获取sdk参数失败!');
        }
  
  })
  
}

本文转载于:

https://blog.csdn.net/m0_56961433/article/details/116135543

https://blog.csdn.net/u014678583/article/details/106024611

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

posted @   林恒  阅读(3155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
历史上的今天:
2021-01-06 uni-app 获取地址位置
欢迎阅读『记录--微信调用jssdk全流程详解』
点击右上角即可分享
微信分享提示