前言
在这篇文章介绍并记录关于Springboot后端代码整合vue前端代码简单实现微信网页自定义分享朋友和朋友圈功能,
注:部分代码是通过网上搜索和自己结合实现的功能,下面就贴上自己的实现步骤。
1、微信开发文档
[https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#5]
具体详细可先观看微信开发文档的实现步骤,通过查看文档大致可知,
实现此分享功能需要有前提查看目录,
1.1 JSSDK使用步骤
微信JS-SDK是微信公众平台 面向网页开发者提供的基于微信内的网页开发工具包。此处略。
1.1.1 步骤一:绑定域名
在实现此功能之前必须要有一个服务器和一个已经备案好了的域名,倘若没有服务器和域名,可以通过使用NATAPP内网穿透的方式来实现,具体可上网搜索学习。
在此之前需要申请了一个测试公众号平台来获取appId和appsecret
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
具体不懂得怎么申请可查看本人另外一篇文章有说明。
接下来就是绑定域名了,需要在JS接口安全域名填写自己的域名
1.1.2 步骤二:引入JS文件
官方说明是可以制醋HTTP和HTTPS方式,在此使用的是HTTP。
由于项目是使用Vue来开发,在此就不是引入JS文件了,而是通过npm的方式。
注:此处本人有个意外的大坑,不知道是不是引错Jssdk,在这处贴上这个错误。
npm install weixin-js-sdk --save //使用这个npm安装后
Vue引用
import wx from ‘weixin-js-sdk’ //Vue里面引入后会出现下面错误
最后通过使用了
npm install weixin-jsapi --save //使用这个npm安装后解决问题,正常显示
npm install axios --save // 在装一个axios
Vue引用
import wx from 'weixin-jsapi' //使用正常浏览器不会报错
import axios from 'axios'
1.1.3 步骤三:通过config接口注入权限验证配置
通过文档的顺序可知,所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用,这个是必不可缺的。在此我是在created钩子写上代码,也可另外写一个JS比较简洁的代码来调用,此处我使用的方式比较粗暴简单吧。
//此处是需用axios 去后端服务器获取的数据并注入config
axios.get('http://域名/项目路径/wx/getAccessTokenAndJsapi?url='+encodeURIComponent(location.href.split('#')[0])).then((response)=>{
var res = response.data;
console.log(res)
wx.config({
debug: true, // 开启调试模式
appId: res.data.appId, // 必填,公众号的唯一标识
timestamp: res.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
signature: res.data.signature,// 必填,签名
jsApiList: [
"updateAppMessageShareData",//自定义“分享给朋友”及“分享到QQ”按钮的分享内容
"updateTimelineShareData",//自定义“分享到朋友圈”及“分享到QQ空间”按钮的分享内容
"onMenuShareWeibo",//获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
] // 必填,需要使用的JS接口列表
})
}).catch((error)=>{
console.log(error)
});
1.1.4 步骤四:通过ready接口处理成功验证
由于此处是使用分享朋友和朋友圈功能,需要将代码写在wx.ready(function(){
}) 里面,
wx.ready(() => {
wx.updateAppMessageShareData({
title: '微信自定义分享朋友', // 分享标题
desc: '微信自定义分享朋友', // 分享描述
link: location.href.split('#')[0], // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl:'http://sjbz.fd.zol-img.com.cn/t_s240x320c/g5/M00/00/03/ChMkJ1fJVzuIYa52AAabqdbC5xQAAU-CgPnq3YABpvB522.jpg',
success: (res) => {
console.log(res);
}
});
wx.updateTimelineShareData({
title: '微信自定义分享到朋友圈', // 分享标题
desc: '微信自定义分享到朋友圈', // 分享描述
link: location.href.split('#')[0], // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl:'http://sjbz.fd.zol-img.com.cn/t_s240x320c/g5/M00/00/03/ChMkJ1fJVzuIYa52AAabqdbC5xQAAU-CgPnq3YABpvB522.jpg',
// imgUrl: require('./logo.jpg'), // 分享图标(不能赋相对路径,一定要是绝对路径)
success: (res) => {
console.log(res);
}
});
});
1.1.5 步骤五:通过error接口处理失败验证
简单粗暴点直接写在下面就好,此处是直接使用微信文档提供的。
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
console.log(res);
});
具体的接口调用说明可以仔细观看开发文档
1.1.6 基本实现代码
import wx from 'weixin-jsapi'
import axios from 'axios'
export default {
created(){
// 自定义config
axios.get('http://域名/项目名称/wx/getAccessTokenAndJsapi?url='+encodeURIComponent(location.href.split('#')[0])).then((response)=>{
var res = response.data;
console.log(res)
wx.config({
debug: true, // 开启调试模式
appId: res.data.appId, // 必填,公众号的唯一标识
timestamp: res.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
signature: res.data.signature,// 必填,签名
jsApiList: [
"updateAppMessageShareData",//自定义“分享给朋友”及“分享到QQ”按钮的分享内容
"updateTimelineShareData",//自定义“分享到朋友圈”及“分享到QQ空间”按钮的分享内容
"onMenuShareWeibo",//获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
] // 必填,需要使用的JS接口列表
})
}).catch((error)=>{
console.log(error)
});
wx.ready(() => {
wx.updateAppMessageShareData({
title: '微信自定义分享朋友', // 分享标题
desc: '微信自定义分享朋友', // 分享描述
link: location.href.split('#')[0], // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl:'http://sjbz.fd.zol-img.com.cn/t_s240x320c/g5/M00/00/03/ChMkJ1fJVzuIYa52AAabqdbC5xQAAU-CgPnq3YABpvB522.jpg',
success: (res) => {
console.log(res);
}
});
wx.updateTimelineShareData({
title: '微信自定义分享到朋友圈', // 分享标题
desc: '微信自定义分享到朋友圈', // 分享描述
link: location.href.split('#')[0], // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl:'http://sjbz.fd.zol-img.com.cn/t_s240x320c/g5/M00/00/03/ChMkJ1fJVzuIYa52AAabqdbC5xQAAU-CgPnq3YABpvB522.jpg',
// imgUrl: require('./logo.jpg'), // 分享图标(不能赋相对路径,一定要是绝对路径)
success: (res) => {
console.log(res);
}
});
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
console.log(res);
});
},
}
注:前端页面基本完毕
2. 后端代码实现
第一步:需要通过测试公众号的appId和appsecret实现获取access_token。
第二步:使用access_token获取jsapi_ticket。
第三步:使用时间戳,随机数,jsapi_ticket和要访问的url按照签名算法拼接字符串。
第四步:对第三步的字符串进行SHA1加密,得到签名。
只贴上实现代码,具体Springboot框架可以通过网上来进行学习。
代码写完后,需要通过maven命令
mvn install -Dmaven.test.skip=true打成Jar包,也可以通过开发工具打包,
然后丢到阿里云服务器上面,再用nginx域名代理。
2.1.1 添加依赖
再此之前需要添加pom.xml依赖
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.2.3</version>
<classifier>jdk15</classifier><!-- jdk版本 -->
</dependency>
2.2.2 步骤一:获取 access_token
public static String getAccessToken(String appId, String appSecret){
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret;
String accessToken = null;
try
{
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
String message = new String(jsonBytes, "UTF-8");
JSONObject jsonObj = JSONObject.fromObject(message);
accessToken = jsonObj.getString("access_token");
}
catch (Exception e)
{
e.printStackTrace();
}
return accessToken;
}
2.2.3 步骤二:获取 jsapi_ticket
public static String getAccessTicket(String access_token) {
String ticket = null;
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ access_token +"&type=jsapi";//这个url链接和参数不能变
try {
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
String message = new String(jsonBytes, "UTF-8");
JSONObject demoJson = JSONObject.fromObject(message);
System.out.println("JSON字符串:"+demoJson);
ticket = demoJson.getString("ticket");
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return ticket;
}
2.2.3 步骤三:SHA1加密
public static String SHA1(String decript) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
2.2.4 整体WxTokenAndJsapiUtil代码
public class WxTokenAndJsapiUtil {
//测试公众号aapId
public static final String appId = "";
//测试公众号密钥appSecret
public static final String appSecret= "";
//当前页面,此处我是直接使用域名来访问当前需要分享的页面
public static final String url = "www.xxx.top";
/**
* 通过appId和appSecret 获取Token
* @param appId
* @param appSecret
* @return
*/
public static String getAccessToken(String appId, String appSecret){
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret;
String accessToken = null;
try
{
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
String message = new String(jsonBytes, "UTF-8");
JSONObject jsonObj = JSONObject.fromObject(message);
accessToken = jsonObj.getString("access_token");
}
catch (Exception e)
{
e.printStackTrace();
}
return accessToken;
}
/**
* 获得ACCESS_TICKET
* @param access_token
* @return
*/
public static String getAccessTicket(String access_token) {
String ticket = null;
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ access_token +"&type=jsapi";//这个url链接和参数不能变
try {
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
String message = new String(jsonBytes, "UTF-8");
JSONObject demoJson = JSONObject.fromObject(message);
System.out.println("JSON字符串:"+demoJson);
ticket = demoJson.getString("ticket");
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return ticket;
}
/**
* 加密
* @param decript
* @return
*/
public static String SHA1(String decript) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
2.2.5 实现分享朋友和朋友圈 controller控制器
@GetMapping(value = "getAccessTokenAndJsapi")
public CommonResponse getAccessTokenAndJsapi(@RequestParam (value = "url",required = false) String url){
log.info(" url = {} ",url);
// 1. 获取access_token
String accessToken = WxTokenAndJsapiUtil.getAccessToken(WxTokenAndJsapiUtil.appId,WxTokenAndJsapiUtil.appSecret);
log.info(" accessToken = {} ",accessToken);
// 2. 获取access_ticket
String accessTicket = WxTokenAndJsapiUtil.getAccessTicket(accessToken);
log.info(" accessTicket = {} ",accessTicket);
// 3. 获取时间戳和随机字符串
String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//随机字符串
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//时间戳
log.info("nonceStr = {} , timestamp = {}",nonceStr,timestamp);
// 4. 获取url
if (!StringUtils.isEmpty(url) && url.length() > 0 ){
}else {
url = WxTokenAndJsapiUtil.url;
}
// 5. 将参数排序并拼接字符串
String str = "jsapi_ticket="+accessTicket+"&noncestr="+nonceStr+"×tamp="+timestamp+"&url="+url;
// 6. 将字符串进行sha1加密
String signature = WxTokenAndJsapiUtil.SHA1(str);
log.info(" signature = {} ",signature);
Map<String,String> map=new HashMap();
map.put("appId",WxTokenAndJsapiUtil.appId);
map.put("timestamp",timestamp);
map.put("accessToken",accessToken);
map.put("ticket",accessTicket);
map.put("nonceStr",nonceStr);
map.put("signature",signature);
JSONObject jsonObject = JSONObject.fromObject(map);
return ResponseVoUtil.success(jsonObject);
}
2.2.6 统一响应数据实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResponse<T> implements Serializable {
private Integer code;
private String message;
private T data;
public CommonResponse(Integer code, String message) {
this.code = code;
this.message = message;
}
}
2.2.7 统一响应数据封装类
public class ResponseVoUtil {
public static CommonResponse success(Object object) {
CommonResponse response = new CommonResponse();
response.setData(object);
response.setCode(0);
response.setMessage("成功");
return response;
}
public static CommonResponse success(){
return success(null);
}
public static CommonResponse error(Integer code,String msg){
CommonResponse response = new CommonResponse();
response.setCode(code);
response.setMessage(msg);
return response;
}
}
2.2.8 访问接口结果
http://自己域名/项目名称/wx/getAccessTokenAndJsapi
注:JS-SDK使用权限签名算法
可参考开发文档的Java的示例代码
http://demo.open.weixin.qq.com/jssdk/sample.zip
此时前端和后端的代码基本完成。
3. 将项目发打包上传服务器
本人使用的打包是使用cmd 的路径加上命令的方式打包
mvn install -Dmaven.test.skip=true
也可以使用开发工具的打包
3.3.1 上传后运行服务器
注:使用的是centos7 系统
java -jar xxxxxx.jar
3.3.2 nginx配置
conf目录下使用 vim nginx.conf
server {
listen 80;
server_name 自己的域名;
#charset koi8-r;
#access_log logs/host.access.log main;
# 静态页面
location / {
add_header Access-Control-Allow-Origin *;
root /usr/local/html/h5; //Vue打包后的项目名存放的位置
# root /usr/local/html/demo;
# root /usr/local/html/dist;
index index.html index.htm;
}
# 服务器代理,在此使用了项目路径名称
# 访问的时候就可以通过域名加/路径
# 例如 域名/hc/wx/getAccessTokenAndJsapi
location /hc {
proxy_pass http://自己的ip地址:项目端口/项目路径; //将ip地址反向给域名代理
# 例如 proxy_pass http://115.xxx.xxx.xxx/hc;
}
4. 分享功能实现展示
在此使用微信点击微信页面点击分享功能
4.4.1 步骤一:微信页面访问域名
4.4.2 步骤二:进入域名后会提示成功
config
updateAppMessageShareData
updateTimelineShareData
4.4.3 步骤三:选中右上角发送朋友
选中右上角发送朋友后显示
4.4.4 步骤三:最终效果
5.结语
大致就把一个Demo案例分享朋友、朋友圈功能案例实现了,
要是有问题可在留言提问,讨论讨论。