【第3期】Springboot:微信公众号分享"朋友圈"
2020-04-19 本文已影响0人
村中一少
原创文章,转载请注明原文章地址,谢谢!
第一步:vue端
1)App.vue类
import sdk from './router/share'; // 引入sdk.
mounted() {
/* eslint-disable */
const url = location.href.split('#')[0];
console.log(url)
const dataForWeixin = {
title: '分享标题', // 分享标题
desc: '分享内容', // 分享内容
linkurl: "http://nhh.ngrok.ibanzhuan.cn", // 分享链接
img: 'http://show.1m2.net/sj/static/img/sharre.jpg', // 分享内容显示的图片(图片必须是正方形的链接)
};
const ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) === 'micromessenger') {
sdk.getJSSDK(url,dataForWeixin); //传入sdk.js需要的参数
} else {
console.log('不是微信浏览器')
sdk.getJSSDK(url,dataForWeixin);
}
},
其中,title、desc、linkurl、img这四个为分享的设置,linkurl应动态获取当前前端页面地址
const dataForWeixin = {
title: '分享标题', // 分享标题
desc: '分享内容', // 分享内容
linkurl: "http://192.168.0.124", // 分享链接
img: 'http://show.1m2.net/sj/static/img/sharre.jpg', // 分享内容显示的图片(图片必须是正方形的链接)
};
2)share.js
import wx from 'weixin-js-sdk'
import axios from 'axios';
// 要用到微信API
function getJSSDK(url, dataForWeixin) {
// 调用后台接口换取参数
axios.get('http://nhc.ngrok.ibanzhuan.cn/app/wechat/initJSSDKConfig', {
params: {
url,
},
}).then((res) => {
console.log(res)
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: res.data.map.appid, // 必填,公众号的唯一标识
timestamp: res.data.map.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.map.nonceStr, // 必填,生成签名的随机串
signature: res.data.map.signature, // 必填,签名
jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline', 'updateAppMessageShareData', 'updateTimelineShareData','onMenuShareQQ','onMenuShareWeibo'] // 必填,需要使用的JS接口列表
});
wx.ready(function () {
wx.onMenuShareAppMessage({
title: dataForWeixin.title,
desc: dataForWeixin.desc,
link: dataForWeixin.linkurl,
imgUrl: dataForWeixin.img,
success: function () {
// 用户确认分享后执行的回调函数
alert('分享成功');
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
alert("errorMSG:"+res);
});
});
});
}
export default {
// 获取JSSDK
getJSSDK,
}
其中,接口
http://nhc.ngrok.ibanzhuan.cn/app/wechat/initJSSDKConfig 为后端地址获取公众号基本配置的接口,在第二步讲述。
第二步:springboot接口
Controller类
package com.qh.app.controller;
import com.qh.app.annotation.CompanyNum;
import com.qh.app.service.WechatService;
import com.qh.app.utils.WechatUtil;
import com.qh.common.response.BaseResponse;
import com.qh.common.utils.AgrLogUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* @Author :nhchun
* @Date: 2020/4/16 9:01
* @Desc:
**/
@CrossOrigin
@RestController
@RequestMapping("/app/wechat")
public class AppWechatController {
public final static Logger logger = AgrLogUtils.getLogger(AppWechatController.class);
@Autowired
WechatService wechatService;
/**
* 获取公众号接入的初始配置
* @param url
* @param companyNum
* @return
*/
@GetMapping("/initJSSDKConfig")
public BaseResponse initJSSDKConfig(@RequestParam(name="url")String url,@CompanyNum String companyNum) {
Map map = wechatService.initJSSDKConfig(url, companyNum);
return BaseResponse.ok().put("map",map);
}
}
Service类
这里我把公众号的一些参数appid、appsecret、jssdk_accesstoken_url、jssdk_getticket_url配置到数据库中了,当然也可以放置在属性文件application.yml中。
package com.qh.app.service.impl;
import com.qh.app.repository.AppConfigRepository;
import com.qh.app.service.WechatService;
import com.qh.app.utils.WechatUtil;
import com.qh.common.entity.ConfigEntity;
import com.qh.common.exception.BizException;
import com.qh.common.exception.ResultEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @Author :nhchun
* @Date: 2020/4/16 15:08
* @Desc:
**/
@Service
public class WechatServiceImpl implements WechatService {
@Autowired
AppConfigRepository configRepository;
@Override
public Map initJSSDKConfig(String url,String companyNum) {
Map<String,String> map = new HashMap<String,String>();
try {
//1 获取数据库配置
ConfigEntity configEntity = getConfigEntity(companyNum);
//2 获取AccessToken
String accessToken = WechatUtil.getJSSDKAccessToken(configEntity.getJssdkAccesstokenUrl(),
configEntity.getAppID(),configEntity.getAppsecret());
//3 获取JssdkGetticket
String jsapiTicket = WechatUtil.getJssdkGetticket(configEntity.getJssdkGetticketUrl(),
accessToken);
//4 签名
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
String nonceStr = UUID.randomUUID().toString();
String signature = WechatUtil.buildJSSDKSignature(jsapiTicket, timestamp, nonceStr, url);
//5 返回
map.put("url", url);
map.put("jsapi_ticket", jsapiTicket);
map.put("nonceStr", nonceStr);
map.put("timestamp", timestamp);
map.put("signature", signature);
map.put("appid", configEntity.getAppID());
}catch(Exception e){
throw new BizException(ResultEnum.FAIL);
}
return map;
}
private ConfigEntity getConfigEntity(String companyNum) {
ConfigEntity configEntity = configRepository.queryByCompanyNum(companyNum);
if (configEntity == null) {
throw new BizException(ResultEnum.COMPANYNUM_NO_CONFIG);
}
return configEntity;
}
}
WechatUtil工具类
package com.qh.app.utils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.qh.common.utils.RedisUtil;
import com.qh.common.utils.StringUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.security.MessageDigest;
import java.util.Map;
/**
* @Author :nhchun
* @Date: 2020/4/16 9:11
* @Desc:
**/
@Component
public class WechatUtil {
public static final String TOKEN ="sckt";
public static boolean validParams(String signature,String timestamp,String nonce){
try {
return SHA1.gen(new String[]{TOKEN, timestamp, nonce}).equals(signature);
} catch (Exception var5) {
return false;
}
}
/**
* 获取access_token
* @return
*/
public static String getJSSDKAccessToken(String JssdkAccesstokenUrl,String AppId,String AppSecret) {
String token = null;
//1 去緩存取
token= RedisUtil.getString("ACESSTOKEN"+AppId);
if(!StringUtils.isNullOrEmpty(token)){
return token;
}
//2 重新获取
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");
}
RedisUtil.setString("ACESSTOKEN"+AppId,token,7200);
return token;
}
public static String getJssdkGetticket(String JssdkGetticketUrl,String accessToken) {
String jsapi_ticket = null;
jsapi_ticket= RedisUtil.getString("jsapi_ticket"+accessToken);
if(!StringUtils.isNullOrEmpty(jsapi_ticket)){
return jsapi_ticket;
}
String url = JssdkGetticketUrl.replaceAll("ACCESS_TOKEN", accessToken);
String json = postRequestForWeiXinService(url);
Map map = jsonToMap(json);
if (map != null) {
jsapi_ticket = (String) map.get("ticket");
}
RedisUtil.setString("jsapi_ticket"+accessToken,jsapi_ticket,7200);
return jsapi_ticket;
}
/**
*@Author: nhc
*@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 + "×tamp=" + 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: nhc
*@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:nhc
*@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: nhchun
*@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;
}
}
这个工具类中access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值,我把它放在redis缓存中了。
分享成功如下
当然,调试过程中,可能还会遇到其它的问题,后续会补充。。。
说明:关注微信公众号【村中一少】,回复“电子书”按照提示信息获取对应书籍目录序号,如回复【1】,即可获得电子书【研磨设计模式.陈臣.王斌.扫描版.pdf】下载地址,所有电子均为免费。
这些电子书仅仅用于学习,如果喜欢请购买正版图书!请支持原版作者!