官网地址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
(1)项目搭建 。。。。。
(2)WxShareController
import com.adbj.service.WxPublicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
/**
* @Description: 微信分享
* @Author: ZhaoLinXuan
* @Date: 2019/8/27 9:56
*/
@Controller
@RequestMapping(value = "/wxShare")
public class WxShareController {
@Autowired
private WxPublicService wxPublicService;
/**
* @param url
* @return
* @throws Exception
*/
@RequestMapping(value = "/getSignature",method = RequestMethod.GET)
@ResponseBody
public Map<String,Object> getSignature(String url) throws Exception {
Map<String,Object> config = wxPublicService.getSignature(url);
return config;
}
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String index() throws Exception {
return "index";
}
}
(3)WxPublicService,RedisService
import org.springframework.data.repository.query.Param;
import java.util.Map;
/**
* @Description: 微信分享后台配置
* @Author: ZhaoLinXuan
* @Date: 2019/8/27 10:32
*/
public interface WxPublicService {
/**
* fetch data by rule url
*
* @param url rule url
* @return Map<String,Object>
*/
Map<String,Object> getSignature(@Param("url") String url);
}
/**
* redis操作Service,
* 对象和数组都以json形式进行存储
*
* @author zlx
* @date 2019/08/10
*/
public interface RedisService {
/**
* 存储数据
*/
void set(String key, String value);
/**
* 获取数据
*/
String get(String key);
/**
* 设置超期时间
*/
boolean expire(String key, long expire);
/**
* 删除数据
*/
void remove(String key);
/**
* 自增操作
* @param delta 自增步长
*/
Long increment(String key, long delta);
Boolean hasKey(String key);
}
(4)WxPublicServiceImpl (AppId,AppSecret需要自己的),RedisServiceImpl
import com.adbj.service.RedisService;
import com.adbj.service.WxPublicService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import utils.WxAccessTokenSinglenton;
import utils.WxJsapiticketSinglenton;
import utils.WxSign;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Description:
* @Author: ZhaoLinXuan
* @Date: 2019/8/27 10:35
*/
@Service
public class WxPublicServiceImpl implements WxPublicService {
private static final Logger LOGGER = LoggerFactory.getLogger(WxPublicServiceImpl.class);
private static final int OUT_TIME=600;
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=AppId&secret=AppSecret";
private static final String TICKETURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=";
@Autowired
private RedisService redisService;
@Override
public Map<String, Object> getSignature(String url) {
Long date = (Long) (System.currentTimeMillis()/1000);
LOGGER.info("date:{}",date);
Map<String,Object> map = new LinkedHashMap<>();
if(StringUtils.isNotBlank(url)){
String access_token=getAccessTokenByRedis(date);
if(access_token.equals("null")||StringUtils.isBlank(access_token)){
map.put("signature","获取签名失败,获取token的IP地址未授权");
return map;
}
//获取ticket的链接
String jsapi_ticket=getJsapiticketByRedis(access_token,date);
//根据参数进行sha1签名
Map<String, String> result = WxSign.sign(jsapi_ticket,url);
map.put("appId","AppId");
map.put("url",result.get("url"));
map.put("timestamp",result.get("timestamp"));
map.put("nonceStr",result.get("nonceStr"));
map.put("signature",result.get("signature"));
}else{
map.put("signature","获取签名失败");
}
return map;
}
public String getAccessTokenByRedis(Long date) {
String result = "";
if(redisService.hasKey("access_token")){
String access_token = redisService.get("access_token");
JSONObject outcome=JSON.parseObject(access_token);
String time = outcome.getString("time");
String token = outcome.getString("token");
LOGGER.info("redis查询token:{},time:{},NEW_DATE:{},剩余时间:{}",token,time,date,date - Long.parseLong(time));
if (token != null && time != null && (date - Long.parseLong(time)) <OUT_TIME) {
LOGGER.info("redis查询 token:{},nowDate:{},time:{},剩余时间:{}",token,date,time,date - Long.parseLong(time));
result = token;
}else {
redisService.remove("access_token");
//获取token的链接
Map<String,Object> tokenBackMap = new RestTemplate().getForObject(TOKEN_URL,Map.class);
//获取到的token
token = String.valueOf(tokenBackMap.get("access_token"));
String time_remaining=String.valueOf(tokenBackMap.get("expires_in"));
JSONObject json=new JSONObject();
json.put("time", date + "");
json.put("token", token);
redisService.set("access_token",json.toJSONString());
LOGGER.info("过期初始化 token:{},nowDate:{},剩余时间:{}",token,date,time_remaining);
result = token;
}
}else {
redisService.remove("access_token");
//获取token的链接
Map<String,Object> tokenBackMap = new RestTemplate().getForObject(TOKEN_URL,Map.class);
//获取到的token
String token = String.valueOf(tokenBackMap.get("access_token"));
String time_remaining=String.valueOf(tokenBackMap.get("expires_in"));
JSONObject json=new JSONObject();
json.put("time", date+ "");
json.put("token", token);
redisService.set("access_token",json.toJSONString());
LOGGER.info("初始化 》》》accessToken:{},nowDate:{},剩余时间:{}",token,date,time_remaining);
result = token;
}
return result;
}
public String getJsapiticketByRedis(String access_token,Long date) {
String result = "";
LOGGER.info("ticket:{}",redisService.hasKey("ticket"));
if(redisService.hasKey("ticket")){
String ticket = redisService.get("ticket");
JSONObject outcome=JSON.parseObject(ticket);
String time = outcome.getString("time");
String jsapi_ticket = outcome.getString("jsapi_ticket");
LOGGER.info("jsapi_ticket:{},time:{},nowDate:{},剩余时间:{}",jsapi_ticket,time,date,date - Long.parseLong(time));
if (ticket != null && time != null && (date - Long.parseLong(time)) < OUT_TIME) {
LOGGER.info("redis查询 jsapi_ticket:{},nowDate:{},time:{},剩余时间:{}",jsapi_ticket,date,time,date - Long.parseLong(time));
result = jsapi_ticket;
}else {
LOGGER.info("删除ticket:{}",ticket);
redisService.remove("ticket");
String getTicketUrl = TICKETURL+access_token+"&type=jsapi";
Map<String,Object> ticketBackMap = new RestTemplate().getForObject(getTicketUrl,Map.class);
//获取到的ticket
jsapi_ticket = String.valueOf(ticketBackMap.get("ticket"));
String time_remaining=String.valueOf(ticketBackMap.get("expires_in"));
JSONObject json=new JSONObject();
json.put("time", date+ "");
json.put("jsapi_ticket", jsapi_ticket);
redisService.set("ticket",json.toJSONString());
LOGGER.info("过期初始化 》》》ticket:{},nowDate:{},剩余时间:{}",jsapi_ticket,date,time_remaining);
result = jsapi_ticket;
}
}else {
redisService.remove("ticket");
String getTicketUrl = TICKETURL+access_token+"&type=jsapi";
Map<String,Object> ticketBackMap = new RestTemplate().getForObject(getTicketUrl,Map.class);
//获取到的ticket
String jsapi_ticket = String.valueOf(ticketBackMap.get("ticket"));
String time_remaining=String.valueOf(ticketBackMap.get("expires_in"));
JSONObject json=new JSONObject();
json.put("time", date+ "");
json.put("jsapi_ticket", jsapi_ticket);
redisService.set("ticket",json.toJSONString());
LOGGER.info("初始化 》》》ticket:{},nowDate:{},剩余时间:{}",jsapi_ticket,date,time_remaining);
result = jsapi_ticket;
}
return result;
}
}
import com.adbj.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* redis操作Service的实现类
*
* @author zlx
* @date 2019/08/14
*/
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void set(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value);
}
@Override
public String get(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
@Override
public boolean expire(String key, long expire) {
return stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
@Override
public void remove(String key) {
stringRedisTemplate.delete(key);
}
@Override
public Long increment(String key, long delta) {
return stringRedisTemplate.opsForValue().increment(key,delta);
}
@Override
public Boolean hasKey(String key) {
return stringRedisTemplate.hasKey(key);
}
}
(5)WxSign(微信提供)
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @Description:
* @Author: ZhaoLinXuan
* @Date: 2019/8/27 11:01
*/
public class WxSign {
public static void main(String[] args) {
String jsapi_ticket = "jsapi_ticket";
// 注意 URL 一定要动态获取,不能 hardcode
String url = "http://example.com";
Map<String, String> ret = sign(jsapi_ticket, url);
for (Map.Entry entry : ret.entrySet()) {
System.out.println(entry.getKey() + ", " + entry.getValue());
}
};
public static Map<String, String> sign(String jsapi_ticket, String url) {
Map<String, String> ret = new HashMap<String, String>();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
System.out.println(string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
private static 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 static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
(6)html页面
<html>
<head>
<title>二笔的微信分享啊!!!</title>
</head>
<body>
<img src="http://www.adbj.vip/img/aa.jpg" alt="logo" style="position: absolute; visibility: hidden;">
跨域请求调用,实现微信分享小图显示测试页
</body>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
<script type="text/javascript">
$(function () {
var shareUrl = window.location.href.split("#")[0];
var appId;
var timestamp;
var nonceStr;
var signature;
var shareTitle = document.title;
var desc = "朋友圈分享功能!!!!!";
var imgUrl = document.getElementsByTagName("img")[0].getAttribute("src");
if(document.getElementsByTagName("img").length>1){
imgUrl = document.getElementsByTagName("img")[1].getAttribute("src");
}
$.ajax({
type: "GET",
url: "/wxShare/getSignature",
data: { url: shareUrl },
dataType: "json",
success: function (data) {
//这里只需要提供当前页面链接的URL,剩下数据全部由后端签名返回,后端可相应调整接口参数
console.log(JSON.stringify(data.result));
appId = data.appId;
timestamp = data.timestamp;
nonceStr = data.nonceStr;
signature = data.signature;
wechatJSDKSignature (appId,timestamp,nonceStr,signature,shareTitle,desc,shareUrl,imgUrl);
}
}
);
});
function wechatJSDKSignature (appId,timestamp,nonceStr,signature,shareTitle,desc,shareUrl,imgUrl){
wx.config({
debug: true,//开启微信调试模式,微信成功分享带图后可关闭
appId: appId,
timestamp: timestamp,
nonceStr: nonceStr,
signature: signature,
jsApiList: [
"updateAppMessageShareData",
"updateTimelineShareData",
"onMenuShareWeibo",
/* "onMenuShareTimeline",
"onMenuShareAppMessage",
"getLocation"*/
]
});
wx.ready(function () {
wx.updateAppMessageShareData({
title: shareTitle,
desc: desc,
link: shareUrl,
imgUrl: imgUrl,
success: function () {
console.log("调用分享给朋友成功");
}
});
wx.updateTimelineShareData({
title: shareTitle,
link: shareUrl,
imgUrl: imgUrl,
success: function () {
console.log("调用分享到朋友圈成功");
}
});
/* wx.onMenuShareTimeline({
title: shareTitle,
desc: desc,
link: shareUrl,
imgUrl: imgUrl,
success: function () {
console.log("调用即将废弃的分享给朋友成功");
}
});
wx.onMenuShareAppMessage({
title: shareTitle,
link: shareUrl,
imgUrl: imgUrl,
success: function () {
console.log("调用即将废弃的分享到朋友圈成功");
}
});*/
wx.onMenuShareWeibo({
title: shareTitle, // 分享标题
desc: desc, // 分享描述
link: shareUrl, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function () {
console.log("调用分享给微博成功");
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
});
wx.error(function (res) {
console.log("程序错误");
});
}
</script>
</html>
(7)结果