微信的分享
准备工作
申请配置微信公众号平台中的JS接口安全域名和网页授权域名,填写你得域名,例如:http://baidu.com/ 这种格式的 后面不要带参数之内的
还有在开发–>基本配置中将你的服务器设置成ip白名单;还有设置开发者密码,开发时需要用到的
后台代码
啥也不说了,直接上代码了。有兴趣的朋友可以去看看官方文档
package com.tcm.Wechat.controller;
import com.alibaba.fastjson.JSONObject;
import com.tcm.Wechat.util.ASEUtil;
import com.tcm.Wechat.util.HttpGet;
import com.tcm.Wechat.util.Parm;
import com.tcm.common.util.ObjectParserUtile;
import com.tcm.common.util.RedisUtil;
import org.jboss.logging.Logger;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/weiXin")
public class WeiXinController {
private static Logger logger = Logger.getLogger(WechatController.class);
@Resource
private RedisUtil redisUtil;
// 获取微信的openid
@PostMapping("/wxOpeid")
public Map WxOpenid(@RequestBody String code) throws Exception {
Map params = new HashMap();
Map map = new HashMap<>();
// 微信公众号平台的 开发者密码
params.put("secret", Parm.APPSECRET);
// 微信公众号平台的 开发者id
params.put("appid", Parm.APPID);
// 获取access_token填写client_credential
params.put("grant_type", "client_credential");
// code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
params.put("code", code.subSequence(0, code.length() - 1));
System.out.println(params);
// 获取用户的openid
String result = HttpGet.httpRequestToString(
"https://api.weixin.qq.com/sns/oauth2/access_token", params);
System.out.println("result>>>>>" + result);
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.get("openid").toString() != null) {
// 加密openid 加密方式随意的,加密是因为分享链接中的openid会传播出去 所以需要加密一下
String openid = ASEUtil.AESencrypt(jsonObject.get("openid").toString());
logger.info("openid>>>>>:"+openid);
map.put("openid", openid);
map.put("status", 200);
} else {
map.put("openid", null);
map.put("status", 500);
}
return map;
}
/**
* 第一步:用户同意授权,获取code(引导关注者打开如下页面:)
* 获取 code、state
*/
@PostMapping("/code")
public static String getStartURLToGetCode() {
Map<String, String> params = new HashMap<String, String>();
params.put("redirect_uri", Parm.REDIRECT_URI);
params.put("appid", Parm.APPID);
params.put("response_type", "code");
// 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
params.put("scope", "snsapi_base ");
// 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
params.put("state", "STATE#wechat_redirect");
// 调起授权页面
String result = HttpGet.httpRequestToString("https://open.weixin.qq.com/connect/oauth2/authorize", params);
System.out.println(result);
return result;
}
// 获取微信token
@PostMapping("/access_token")
public String access_Token() {
Map<String, String> params = new HashMap<String, String>();
// 判断是否在redis缓存中 存在 false 不存在 true 存在
boolean access_Token = redisUtil.hashKey("access_Token");
System.out.println(access_Token);
if (access_Token == false) {
// 开发者密码
params.put("secret", Parm.APPSECRET);
// 开发者id
params.put("appid", Parm.APPID);
// 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
params.put("grant_type", "client_credential");
// 获取token 两个小时过期
String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/token", params);
System.out.println("result>>>>>>:" + result);
JSONObject jsonObject = JSONObject.parseObject(result);
System.out.println("jsonObject:>>>>" + jsonObject);
String token = jsonObject.get("access_token").toString();
System.out.println("token:>>>>" + token);
// 放入数据时设置redis缓存过期时间 一个半小时
redisUtil.set("access_Token", token, 5400);
return token;
} else {
return redisUtil.get("access_Token").toString();
}
}
// 获取微信ticket
@PostMapping("/get_ticket")
public String getTicket() {
// 判断是否在redis缓存中 存在 false 不存在 true 存在
boolean access_Token = redisUtil.hashKey("get_ticket");
if (access_Token == false) {
// 获取token
String token = access_Token();
Map<String, String> params = new HashMap<String, String>();
// 调用类型
params.put("type", "jsapi");
params.put("access_token", token);
params.put("grant_type", "client_credential");
String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/ticket/getticket", params);
JSONObject jsonTicket = JSONObject.parseObject(result);
// 放入数据时设置redis缓存过期时间 一个半小时
redisUtil.set("get_ticket", jsonTicket.get("ticket").toString(), 5400);
return jsonTicket.get("ticket").toString();
} else {
// 获取redis缓存的数据
return redisUtil.get("get_ticket").toString();
}
}
// 微信签名算法
@RequestMapping(value = "/get_signature")
public Object getSignature(@RequestBody String url) throws UnsupportedEncodingException {
// 前端的页面地址
url = URLDecoder.decode(url.subSequence(0, url.length() - 1).toString(), "UTF-8");
System.out.println("不存在");
Map<String, String> ret = new HashMap<String, String>();
System.out.println("url>>>>>:" + url);
String wxTicket = getTicket();
// 获取随机数 我用的是 UUID
String nonce_str = create_nonce_str();
// 时间戳 要秒为单位的
String timestamp = create_timestamp();
String str;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
str = "jsapi_ticket=" + wxTicket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
try {
// 微信签名 就是将上面的参数str加密
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(str.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", wxTicket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
ret.put("appId", Parm.APPID);
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);
}
// 获取用户信息
@PostMapping("/userinfo")
public JSONObject user_info(@RequestParam String openid) throws Exception {
Map<String, String> params = new HashMap<String, String>();
String token = access_Token();
params.put("access_token", token);
params.put("openid", openid);
params.put("lang", "zh_CN");
String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/user/info", params);
System.out.println("result>>>>:" + result);
JSONObject jsonObject = JSONObject.parseObject(result);
return jsonObject;
}
// 批量获取用户信息
@PostMapping("/userInfos")
public JSONObject user_infos(@RequestParam String token) {
Map<String, String> params = new HashMap<String, String>();
params.put("access_token", token);
String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/user/info/batchget", params);
System.out.println("result>>>>:" + result);
JSONObject jsonObject = JSONObject.parseObject(result);
return jsonObject;
}
}
vue前端
vue需要安装一下 微信的依赖
cnpm install weixin-js-sdk
urlZy.js (转义js,后台加密的openid传到前端时 有些特殊字符被转义了,如果不转回来的话,后台解密就会有问题,所以保险起见转换一下吧)
let urlzy = function urlzy(url) {
// 将转义字符转换成对应的特殊 字符
url = url.replace("%20", "+").replace("%3D", "=").replace("%2F", "/").replace("%3F", "?").replace("%25", "%").replace("%23", "#").replace("%26", "&");
return url;
}
export default {
urlzy //多个方法在此处json中export出去
}
openidApi.js (获取用户的openid,并加密)
// 后台接口
import { wxOpeid } from "../../api/api";
// 分享的js
import configApi from "./configApi";
// 转义字符
import urlzy from "./urlZy";
// 截取当前连接中的 code值
function getUrlKey(name) {
console.log("getUrlKey");
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
location.href
) || [, ""])[1].replace(/\+/g, "%20")
) || null
);
}
// 获取code
function getCodeApi() {
console.log("getCodeApi");
let urlNow = encodeURIComponent(window.location.href);
let scope = "snsapi_userinfo"; //snsapi_userinfo //静默授权 用户无感知
let appid = ""; // 开发者id 你可以直接写在前端 也可以 通过后台接口 传
let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${urlNow}&response_type=code&scope=${scope}&state=STATE#wechat_redirect"`;
let code = window.location.replace(url);
console.log(code);
let cade = getUrlKey(code);
console.log(cade);
}
// 获取openid
let getOpenid = function getOpenid() {
console.log("getOpenid");
var that = this;
that.loading = true;
//返回值
let code = getUrlKey("code");
if (code) {
console.log(code);
wxOpeid(code).then(ref => {
if (ref.data.status == 200) {
// 清除缓存中的 openid
sessionStorage.removeItem("openid");
// 将openid 放入缓存中
sessionStorage.setItem("openid", urlzy.urlzy(ref.data.openid));
// 分享js
configApi.fenxiang();
} else {
that.$message.error("网络不好,请刷新或重新进入");
}
});
} else {
getCodeApi();
}
}
export default {
getOpenid
}
configApi.js (最新的那两个分享接口,暂时没发现他有分享成功后的回调函数,只有配置分享标题等成功后的回调函数)
// 引入微信
import wx from "weixin-js-sdk";
import urlzy from "./urlZy";
import { get_signature, wxShare } from "../../api/api";
let fenxiang = function fenxiang() {
console.log("分享");
var that = this;
let urls = location.href.split("#")[0];
console.log(urls);
// 判断访问的地址 是否 有openid 有就是分享出去的链接
if (urls.indexOf("openid") != -1) {
// 将链接中的转义字符转成字符串
let lurl = urlzy.urlzy(urls);
console.log(lurl);
var openid = location.href.split("&")[1].split("=");
console.log("openid:" + openid);
var plat = location.href.split("&")[2].split("=");
var platform;
console.log("plat:" + plat);
if (plat[1] == 1) {
platform = "微信好友";
} else if (plat[1] == 2) {
platform = "微信朋友圈";
} else if (plat[1] == 3) {
platform = "QQ空间";
} else if (plat[1] == 4) {
platform = "QQ";
} else if (plat[1] == 5) {
platform = "微博";
}
let params = {
browseOpenid: sessionStorage.getItem("openid"), // 浏览人的openid
openid: urlzy.urlzy(openid[1]), // 分享人的openid
status: 0, // 0 分享链接处 1 通过其他人链接进来的
url: lurl, // 分享的链接
platform: platform // 分享的平台
};
console.log(params);
// 保存浏览信息
wxShare(params).then(ref => {
if (ref.data.status == 200) {
// 跳转链接 去掉openid 等参数
window.location.href = location.href.split("&")[0];
}
});
} else {
// 获取微信分享签名的后台接口 传当前页面url地址
get_signature(encodeURIComponent(location.href.split("#")[0])).then(ref => {
console.log(ref);
// 微信jsapi中的config接口注入权限验证配置
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: ref.data.appId, // 必填,公众号的唯一标识
timestamp: ref.data.timestamp, // 必填,生成签名的时间戳
nonceStr: ref.data.nonceStr, // 必填,生成签名的随机串
signature: ref.data.signature, // 必填,签名
jsApiList: [
"onMenuShareWeibo", //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
"onMenuShareAppMessage", // 微信好友
"onMenuShareTimeline", // 微信朋友圈
"onMenuShareQQ", // QQ
"onMenuShareQZone", // QQ空间
"chooseWXPay" //微信支付
] // 必填,需要使用的JS接口列表
});
});
// 检验js接口
wx.checkJsApi({
jsApiList: [
"onMenuShareWeibo", //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
"onMenuShareAppMessage", // 微信好友
"onMenuShareTimeline", // 微信朋友圈
"onMenuShareQQ", // QQ
"onMenuShareQZone", // QQ空间
"chooseWXPay" //微信支付
],
success: function (res) {
//这里没返应 也没提示,也不会进断点
}
});
//通过ready接口处理成功验证
wx.ready(function () {
// 修改分享链接 将当前用户的openid 添加到分享链接中 方便记录谁通过该用户的分享进入
let linkurl = location.href.split("&")[0] + "&openid=" + sessionStorage.getItem("openid");
console.log(linkurl);
// 获取“分享给朋友”按钮点击状态及自定义分享内容接口(即将废弃)
wx.onMenuShareAppMessage({
title: "国医小镇预报名", // 分享标题
desc: "国医小镇预报名", // 分享描述
link: linkurl + "&app=1", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
success: function (ref) {
setTimeout(function () {
//回调要执行的代码
// status 1 分享 0 预览(通过他人分享进入的)
let params = {
openid: sessionStorage.getItem("openid"),
status: 1,
url: linkurl,
platform: "微信好友"
};
console.log(params);
wxShare(params).then(ref => {
if (ref.data.status == 200) {
// 跳转链接 去掉openid 等参数
window.location.href = location.href.split("&")[0];
}
});
}, 500);
// 用户确定分享后执行的回调函数
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
}
});
// 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口(即将废弃)
wx.onMenuShareTimeline({
title: "国医小镇预报名", // 分享标题
desc: "国医小镇预报名", // 分享描述
link: linkurl + "&app=2", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
success: function (ref) {
setTimeout(function () {
//回调要执行的代码
// status 1 分享 0 预览(通过他人分享进入的)
let params = {
openid: sessionStorage.getItem("openid"),
status: 1,
url: linkurl,
platform: "微信朋友圈"
};
console.log(params);
wxShare(params).then(ref => {
if (ref.data.status == 200) {
// 跳转链接 去掉openid 等参数
window.location.href = location.href.split("&")[0];
}
});
}, 500);
// 用户确定分享后执行的回调函数
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
}
});
// 获取“分享到QQ空间”按钮点击状态及自定义分享内容接口(即将废弃)
wx.onMenuShareQZone({
title: "国医小镇预报名", // 分享标题
desc: "国医小镇预报名", // 分享描述
link: linkurl + "&app=3", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
success: function (ref) {
setTimeout(function () {
//回调要执行的代码
// status 1 分享 0 预览(通过他人分享进入的)
let params = {
openid: sessionStorage.getItem("openid"),
status: 1,
url: linkurl,
platform: "QQ空间"
};
console.log(params);
wxShare(params).then(ref => {
if (ref.data.status == 200) {
// 跳转链接 去掉openid 等参数
window.location.href = location.href.split("&")[0];
}
});
}, 500);
// 用户确定分享后执行的回调函数
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
}
});
// 获取“分享到QQ”按钮点击状态及自定义分享内容接口(即将废弃)
wx.onMenuShareQQ({
title: "国医小镇预报名", // 分享标题
desc: "国医小镇预报名", // 分享描述
link: linkurl + "&app=4", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
success: function (ref) {
setTimeout(function () {
//回调要执行的代码
// status 1 分享 0 预览(通过他人分享进入的)
let params = {
openid: sessionStorage.getItem("openid"),
status: 1,
url: linkurl,
platform: "QQ"
};
console.log(params);
wxShare(params).then(ref => {
if (ref.data.status == 200) {
// 跳转链接 去掉openid 等参数
window.location.href = location.href.split("&")[0];
}
});
}, 500);
// 用户确定分享后执行的回调函数
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
}
});
// 分享微博
wx.onMenuShareWeibo({
title: "国医小镇预报名", // 分享标题
desc: "国医小镇预报名", // 分享描述
link: linkurl + "&app=5", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
success: function (ref) {
setTimeout(function () {
//回调要执行的代码
let params = {
openid: sessionStorage.getItem("openid"),
status: 1,
url: linkurl,
platform: "微博"
};
wxShare(params).then(ref => {
if (ref.data.status == 200) {
// 跳转链接 去掉openid 等参数
window.location.href = location.href.split("&")[0];
}
});
}, 500);
// 用户确定分享后执行的回调函数
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
}
});
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
wx.error(function (res) {
//config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
}
}
export default {
fenxiang //多个方法在此处json中export出去
}