去微信官方文档了解。
首先:去微信公众号平台测试系统拿到appID和appsecret,并且配置js域名
如果测试暂时没有域名可以参考下面链接教程
根据文档描述准备签名等数据
微信相关配置 :
package io.lease.common.config;
/**
* Created by xxs on 2021/11/18 20:12
*
* @Description
*/
public class WxShareConfig {
/**
* 微信appid
*/
public static final String appid = "wx8d1eab965e66105a";
/**
* 微信appsecret
*/
public static final String appsecret = "8e9bc82ce46195795a8a5950859679b4";
/**
* 获取token请求地址
*/
public static final String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?";
/**
* 获取jsapi_ticket请求地址
*/
public static final String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?";
}
请求封装:
package io.lease.modules.weixin.wxShare;
import com.alibaba.fastjson.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by xxs on 2021/11/18 17:04
*
* @Description
*/
public class WeixinUtil {
public static JSONObject doGet(String httpurl) {
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
String result = null;// 返回结果字符串
try {
// 创建远程url连接对象
URL url = new URL(httpurl);
// 通过远程url连接对象打开一个连接,强转成httpURLConnection类
connection = (HttpURLConnection) url.openConnection();
// 设置连接方式:get
connection.setRequestMethod("GET");
// 设置连接主机服务器的超时时间:15000毫秒
connection.setConnectTimeout(15000);
// 设置读取远程返回的数据时间:60000毫秒
connection.setReadTimeout(60000);
// 发送请求
connection.connect();
// 通过connection连接,获取输入流
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
// 封装输入流is,并指定字符集
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
// 存放数据
StringBuffer sbf = new StringBuffer();
String temp = null;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
connection.disconnect();// 关闭远程连接
}
JSONObject jsonObject = JSONObject.parseObject(result);
return jsonObject;
}
}
签名数据返回对象封装:
package io.lease.modules.sys.vo;
import lombok.Data;
/**
* Created by xxs on 2021/11/18 16:03
*
* @Description 分享信息
*/
@Data
public class ShareInfoVO {
private String appId;
private String nonceStr;
private String timeStamp;
private String sign;
}
package io.lease.modules.weixin.wxShare;
import com.alibaba.fastjson.JSONObject;
import io.lease.common.config.WxShareConfig;
import io.lease.modules.sys.vo.ShareInfoVO;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.UUID;
/**
* Created by xxs on 2021/11/18 17:01
*
* @Description
*/
public class WXShare {
public static String byteToHex(final byte[] hash) {
StringBuffer stringBuffer = new StringBuffer();
for (int i=0; i<hash.length;i++){
String hexString = Integer.toHexString(hash[i] & 0xFF);
if (hexString.length() < 2){
stringBuffer.append(0);
}
stringBuffer.append(hexString);
}
return stringBuffer.toString();
}
}
签名数据获取,封装service接口
package io.lease.modules.weixin.wxShare;
import io.lease.modules.sys.dto.ShareInfoDTO;
import io.lease.modules.sys.vo.ShareInfoVO;
/**
* Created by xxs on 2021/11/18 20:16
*
* @Description
*/
public interface WXShareService {
/**
* @param shareInfoDTO
* @Description: 获取微信配置
*/
ShareInfoVO getWxConfig(ShareInfoDTO shareInfoDTO);
}
实现接口(核心):
package io.lease.modules.weixin.wxShare;
import com.alibaba.fastjson.JSONObject;
import io.lease.common.config.WxShareConfig;
import io.lease.modules.sys.dto.ShareInfoDTO;
import io.lease.modules.sys.vo.ShareInfoVO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
/**
* Created by xxs on 2021/11/18 20:17
*
* @Description
*/
@Service
public class WXShareServiceImpl implements WXShareService {
private static final Logger logger = LoggerFactory.getLogger(WXShareServiceImpl.class);
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
private static final String JS_API_TICKET = "JS_API_TICKET";
@Override
public ShareInfoVO getWxConfig(ShareInfoDTO shareInfoDTO) {
ShareInfoVO shareInfoVO = new ShareInfoVO();
String timestamp = getTimestamp(); // 必填,生成签名的时间戳
String nonceStr = getNonceStr(); // 必填,生成签名的随机串
//获取jsapi_ticket
String jsApiTicket = getJsApiTicket();
//生成JS-SDK权限验证的签名
String signature = "";
//注意这里参数名必须全部小写,且必须有序
String sign = "jsapi_ticket=" + jsApiTicket
+"&noncestr=" + nonceStr
+ "×tamp=" + timestamp
+"&url=" + shareInfoDTO.getUrl();
try{
//进行sha1签名
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(sign.getBytes("UTF-8"));
signature = WXShare.byteToHex(crypt.digest());
}catch (NoSuchAlgorithmException e){
e.printStackTrace();
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
shareInfoVO.setAppId(WxShareConfig.appid);
shareInfoVO.setTimeStamp(timestamp);
shareInfoVO.setNonceStr(nonceStr);
shareInfoVO.setSign(signature);
return shareInfoVO;
}
/**
* @param
* @Description: 获取token
*/
private String getAccessToken() {
String accessToken = redisTemplate.opsForValue().get(ACCESS_TOKEN);
if (!StringUtils.isNotBlank(accessToken)){
//缓存无,重新获取token
String url = WxShareConfig.getTokenUrl
+ "grant_type=client_credential&appid=" + WxShareConfig.appid + "&secret=" + WxShareConfig.appsecret;
JSONObject json = WeixinUtil.doGet(url);
if (json != null) {
accessToken = json.getString("access_token");
Integer time = Integer.parseInt(json.getString("expires_in"));
redisTemplate.opsForValue().set(ACCESS_TOKEN,accessToken,time - 3600);
}
}
logger.info("getAccessToken access_token: {}", accessToken);
return accessToken.trim();
}
/**
* @param
* @Description: 获取ticket
*/
private String getJsApiTicket() {
String jsApiTicket = redisTemplate.opsForValue().get(JS_API_TICKET);
if (!StringUtils.isNotBlank(jsApiTicket)){
//获取全局的access_token,唯一票据
String accessToken = getAccessToken();
if (StringUtils.isNotBlank(accessToken)) {
//获取jsapi_ticket
String url = WxShareConfig.getTicketUrl+"access_token="+accessToken+"&type=jsapi";
JSONObject json = WeixinUtil.doGet(url);
if (json != null) {
jsApiTicket = json.getString("ticket");
Integer time = Integer.parseInt(json.getString("expires_in"));
redisTemplate.opsForValue().set(JS_API_TICKET,jsApiTicket,time - 3600);
}
}
}
logger.info("getJsApiTicket jsApiTicket: {}", jsApiTicket);
return jsApiTicket.trim();
}
/**
* @param
* @Description: 获取随机字符串
*/
private static String getNonceStr() {
return UUID.randomUUID().toString();
}
/**
* @param
* @Description: 获取时间戳
*/
private static String getTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
public static void main(String[] args) {
System.out.println(getNonceStr() );
}
}
controller:
package io.lease.controller;
@RestController
@RequestMapping("/api/goods")
@Api(tags = "商品接口")
public class ApiGoodsController {
private static final Logger log = LoggerFactory.getLogger(ApiGoodsController.class);
@Autowired
private WXShareService wxShareService;
@PostMapping("getShareInfo")
@ApiOperation("获取分享配置信息 - H5")
public R getShareInfo(@RequestBody ShareInfoDTO shareInfoDTO){
log.info("getShareInfo shareInfoDTO: {}", JSONObject.toJSONString(shareInfoDTO));
return R.ok(wxShareService.getWxConfig(shareInfoDTO));
}
}
写一个页面测试:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>分享</title>
</head>
<body></body>
</html>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
/* 获取微信配置并注入 */
(function getWxConfig(){
let data = {
url: location.href
}
$.ajax({
url:"http://xxs.free.idcfengye.com/lease-api/api/goods/getShareInfo",
type:"post",
dataType:"json",
data:JSON.stringify(data),
headers:{
"Content-Type":"application/json"
},
success:res=>{
const { data } = res
wx.config({
debug: true,
appId: data.appId,
timestamp: data.timeStamp,
nonceStr: data.nonceStr,
signature: data.sign,
jsApiList: ["updateAppMessageShareData","updateTimelineShareData"]
});
}
})
})()
wx.ready(()=>{
let shareConfig = {
title:"分享标题",
desc:"分享内容",
link:"http://xxs.free.idcfengye.com/h5Share.html", //必须是JS接口安全域名
imgUrl:"https://img2.baidu.com/it/u=3989369665,3150123550&fm=253&fmt=auto&app=120&f=JPEG?w=690&h=460"
}
/* 分享给朋友 */
wx.updateAppMessageShareData(shareConfig)
/* 分享到朋友圈 */
wx.updateTimelineShareData(shareConfig)
})
</script>
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
写测试接口访问页面:
package io.lease.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
/**
* Created by xxs on 2021/11/18 17:57
*
* @Description
*/
@Controller
public class TestController {
@RequestMapping("/test")
public String test(HttpServletResponse response) {
return "H5Share";
}
}
访问测试接口即可。
http://xxs.free.idcfengye.com/lease-api/test
综合文档及下图可知,成功了。
若页面url拼接多个参数时,会出现的问题及解决方案: