这里写目录标题
前台
// 安装js-sdk 本文中使用的版本是 1.6.0
npm i weixin-js-sdk,
方法定义
ios使用JS-SDK报错:config:invalid signature
vue 单页应用中 ,使用 history 模式时,在 ios 中,使用JS-SDK报错:config:invalid signature
ios中,必须用第一次进入的链接地址去获取签名,通过路由跳转的页面无法获取正确签名。
如:进入系统后第一个页面是A,点击A中的按钮,通过路由跳转到B后,仍然要用A的url去签名,B的url无法获取正确签名
代码如下:
router.beforeEach(async (to, from, next) => {
iosWxSign(to, from, next)
})
// ios授权时无法获取到正确的url,用此方法作兼容
const iosWxSign = (to, from, next) => {
// window.__wxjs_is_wkwebview:true为 IOS 设备;false为 安卓 设备
if (window.__wxjs_is_wkwebview) {
// wechat=1,表示页面已自动刷新过一次,并添加了wechat标识
if (to.query.wechat !== undefined && to.query.wechat === '1') {
window.wechaturl = window.location.href;
console.log('window.wechaturl:', window.wechaturl);
} else {
if (window.wechaturl) {
// 不是首次进入
} else {
// 重要步骤!!!当页面被点击刷新后wechaturl是null,这个时候就要把当前的页面再做上小标记。记录一下。
const host = 'http://' + window.location.host + '自己项目的根路径';
let url = host + to.fullPath;
if (url.indexOf('?') >= 0) {
url += '&wechat=1';
} else {
url += '?wechat=1';
}
window.wechaturl = url;
setTimeout(function () {
// 相当于加载一下当前界面,如果不执行这一步的话,微信只是重新渲染了dom,而不重新执行wx.config;
location.replace(url);
}, 300);
}
}
}
};
使用JS-SDK
// 获取微信JS-SDK使用权限的签名,并通过 config 接口注入权限验证配置
// 要使用微信JS-SDK功能的页面,必须先调用此方法
export const getWxSignInfo = async () => {
let wechaturl = window.location.href;
if (window.wechaturl !== undefined) {
wechaturl = window.wechaturl;
}
console.log('签名进去->' + wechaturl);
const [err, res] = await axios.asyncPost('api/wechat/getWxSignInfo', { url: encodeURIComponent(wechaturl.split('#')[0]) });
if (!err && res) {
if (res.data.code === 0) { // 接口调用成功(我自己业务定义的,不是微信的错误码)
// 注入权限验证配置
wx.config({
debug: window.provinceOperator.sdk_debug, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: res.data.sign.appId, // 必填,公众号的唯一标识
timestamp: res.data.sign.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.sign.nonceStr, // 必填,生成签名的随机串
signature: res.data.sign.signature, // 必填,签名
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'] // 必填,需要使用的JS接口列表
});
} else {
// 接口失败报错信息
this.$dialog.alert({ message: res.data.msg }).then(() => {}).catch(() => {});
}
}
};
// 微信分享功能
export const wxShare = (title = document.title, desc = '', link = location.href, imgUrl) => {
console.log('link: ', link);
// “分享给朋友”及“分享到QQ”
wx.ready(() => { // 需在用户可能点击分享按钮前就先调用
wx.updateAppMessageShareData({
title, // 分享标题
desc, // 分享描述
link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl, // 分享图标
success: function () {
// 设置成功
}
});
// “分享到朋友圈”及“分享到QQ空间”
wx.updateTimelineShareData({
title, // 分享标题
desc, // 分享描述
link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl, // 分享图标
success: function () {
// 设置成功
}
});
});
};
async created() {
await getWxSignInfo();
wxShare('周报分享', '快来看看TA的家庭周报吧', location.href, '头像url');
},
后台
/**
* 获取微信JsSDK签名信息
* @param param
* @param request
* @return
*/
@RequestMapping(value = "/getWxSignInfo", method = RequestMethod.POST)
public R WeekRecordShare(@RequestBody Map<String,Object> param, HttpServletRequest request) {
String url = StringUtils.toStrOrNull(param.get("url"));
if(StringUtils.isEmpty(url)){
return R.error("url不能为空!");
}
Map<String, Object> params = new HashMap<>();
params.put("url",url);
//获取access_token
String accessToken = wechatService.getAccessToken(params, request);
if(StringUtils.isEmpty(accessToken)){
return R.error("获取access_token失败!");
}
//拿token换取ticket
params.put("access_token",accessToken);
String jsapi_ticket = wechatService.getTicket(params, request);
if(StringUtils.isEmpty(jsapi_ticket)){
return R.error("获取ticket失败!");
}
//返回签名信息
params.put("jsapi_ticket",jsapi_ticket);
params.put("channel",param.get("channel"));
return wechatService.getJsSign(params, request);
}
WechatService.java
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 获取access_token
* @param request
* @return access_token
*/
@Override
public String getAccessToken(Map<String, Object> params,HttpServletRequest request) {
String access_token = "";
// 公众号的 appId 和 secret
String newAppId = appId;
String newSecret = secret;
try {
Cache tokenCache = CacheService.getCacheInfo("access_token");
//缓存中access_token为空或者已失效,则重新获取access_token
if(tokenCache == null || tokenCache.isExpired() ){
JSONObject accessTokenData = WeChatMsg.getAccessToken(newAppId, newSecret);
//查询成功,则将access_token放置缓存中
if(accessTokenData != null){
access_token = String.valueOf(accessTokenData.get("access_token"));//token
Integer expires_in = Integer.valueOf(accessTokenData.get("expires_in").toString());//失效时间
//为防止token失效,全局缓存失效时间比微信官方失效时间早2min
CacheService.clearOnly("access_token");
Cache cache = new Cache("access_token",access_token,(expires_in-120)*1000,false);
CacheService.putCache("access_token",cache);
}
}else{
//直接从缓存中获取access_token
Cache cacheInfo = CacheService.getCacheInfo("access_token");
access_token = String.valueOf(cacheInfo.getValue());
}
}catch (Exception e){
e.printStackTrace();
logger.error("获取access_token失败!原因:"+e.getMessage());
return null;
}
return access_token;
}
/**
* 获取ticket
* @param request
* @return ticket
*/
@Override
public String getTicket(Map<String, Object> params,HttpServletRequest request) {
String ticket = "";
try {
Cache ticketCache = CacheService.getCacheInfo("ticket");
//缓存中ticket为空或者已失效,则重新获取ticket
if(ticketCache == null || ticketCache.isExpired() ){
String access_token = params.get("access_token").toString();
JSONObject ticketData = WeChatMsg.getTicket(access_token);
//查询成功,则将ticket放置缓存中
if(ticketData != null && "0".equals(String.valueOf(ticketData.get("errcode")))){
ticket = String.valueOf(ticketData.get("ticket"));//ticket
Integer expires_in = Integer.valueOf(ticketData.get("expires_in").toString());//失效时间
CacheService.clearOnly("ticket");
//为防止ticket失效,全局缓存失效时间比微信官方失效时间早2min
Cache cache = new Cache("ticket",ticket,(expires_in-120)*1000,false);
CacheService.putCache("ticket",cache);
}
}else{
//直接从缓存中获取ticket
Cache cacheInfo = CacheService.getCacheInfo("ticket");
ticket = String.valueOf(cacheInfo.getValue());
}
}catch (Exception e){
e.printStackTrace();
logger.error("获取ticket失败!原因:"+e.getMessage());
return null;
}
return ticket;
}
WeChatMsg.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;;
import java.util.*;
/**
* 通过appId和secret获取access_token
* https://api.weixin.qq.com/cgi-bin/token
* @param appid
* @param secret
* @return
*/
public static JSONObject getAccessToken(String appid, String secret) {
JSONObject jsonObject = null;
try {
String result = CustomizedHttpClient.get("https://api.weixin.qq.com/cgi-bin/token"+"?appid="+appid+"&secret="+secret+"&grant_type=client_credential");
jsonObject = JSON.parseObject(result);
} catch (Exception e) {
e.printStackTrace();
logger.error("通过appId和secret获取access_token失败!原因:"+e.getMessage());
}
return jsonObject;
}
/**
* 通过access_token获取ticket
* https://api.weixin.qq.com/cgi-bin/ticket/getticket
* @param accessToken
* @return
*/
public static JSONObject getTicket(String accessToken) {
JSONObject ticketData = null;
try {
String result = CustomizedHttpClient.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket"+"?access_token="+accessToken+"&type=jsapi");
ticketData = JSON.parseObject(result);
} catch (Exception e) {
e.printStackTrace();
logger.error("通过access_token获取ticket失败!原因:"+e.getMessage());
}
return ticketData;
}
Cache.java
public class Cache {
private String key;//缓存ID
private Object value;//缓存数据
private long timeOut;//更新时间
private boolean expired; //是否终止
public Cache() {
super();
}
public Cache(String key, Object value, long timeOut, boolean expired) {
this.key = key;
this.value = value;
this.timeOut = timeOut;
this.expired = expired;
}
public String getKey() {
return key;
}
public long getTimeOut() {
return timeOut;
}
public Object getValue() {
return value;
}
public void setKey(String string) {
key = string;
}
public void setTimeOut(long l) {
timeOut = l;
}
public void setValue(Object object) {
value = object;
}
public boolean isExpired() {
return expired;
}
public void setExpired(boolean b) {
expired = b;
}
}
CacheService .java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @Describe 将数据存入缓存处理类
*/
public class CacheService {
private static HashMap cacheMap = new HashMap();
//单实例构造方法
private CacheService() {
super();
}
//得到缓存。同步静态方法
private synchronized static Cache getCache(String key) {
return (Cache) cacheMap.get(key);
}
//判断是否存在一个缓存
private synchronized static boolean hasCache(String key) {
return cacheMap.containsKey(key);
}
//清除所有缓存
public synchronized static void clearAll() {
cacheMap.clear();
}
//清除某一类特定缓存,通过遍历HASHMAP下的所有对象,来判断它的KEY与传入的TYPE是否匹配
public synchronized static void clearAll(String type) {
Iterator i = cacheMap.entrySet().iterator();
String key;
ArrayList arr = new ArrayList();
try {
while (i.hasNext()) {
Map.Entry entry = (Map.Entry) i.next();
key = (String) entry.getKey();
if (key.startsWith(type)) { //如果匹配则删除掉
arr.add(key);
}
}
for (int k = 0; k <arr.size(); k++) {
clearOnly(arr.get(k).toString());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
//清除指定的缓存
public synchronized static void clearOnly(String key) {
cacheMap.remove(key);
}
//载入缓存
public synchronized static void putCache(String key, Cache obj) {
obj.setTimeOut(obj.getTimeOut() + System.currentTimeMillis()); //设置缓存失效时间
cacheMap.put(key, obj);
}
//获取缓存信息
public static Cache getCacheInfo(String key) {
if (hasCache(key)) {
Cache cache = getCache(key);
if (cacheExpired(cache)) { //调用判断是否终止方法
cache.setExpired(true);
}
return cache;
}else{
return null;
}
}
//判断缓存是否终止
public static boolean cacheExpired(Cache cache) {
if (null == cache) { //传入的缓存不存在
return false;
}
long nowDt = System.currentTimeMillis(); //系统当前的毫秒数
long cacheDt = cache.getTimeOut(); //缓存内的过期毫秒数
if (cacheDt <= 0||cacheDt>nowDt) { //过期时间小于等于零时,或者过期时间大于当前时间时,则为FALSE
return false;
} else { //大于过期时间 即过期
return true;
}
}
//获取缓存中的大小
public static int getCacheSize() {
return cacheMap.size();
}
}