背景描述:
公司有個需求,將一個微網站鏈接,網頁,外部網站在微信中打開,然後分享給其他人,但是在分享過程中發現分享完微信中顯示的是這樣子,
標題有了,但是簡介顯示網址,圖片沒有,而我想要的是上面另一種樣式。標題,簡介和圖片都可自定義的。由此產生了這篇文章。
必備要求:
- 有公衆號,公衆號必須認證過,如果不確定認證沒認證過,就看下圖,在微信公衆平臺https://mp.weixin.qq.com最下面接口權限中看分享接口後面狀態是已獲得還是未獲得,已獲得纔可以用,未獲得說明沒有認證過,就不用往下看了。
2.必須要有已備案的域名和服務器。實現這個功能需要調用後臺接口,而且接口的域名和分享網站,網頁的域名是一樣的。
參考資料:
實現過程:
上面準備工作做完以後開始實現。
- 首先可以先閱讀以下微信的JS-SDK的說明文檔
微信JS-SDK說明文檔 - 綁定域名,登錄微信公衆平臺,進入“公衆號設置”的功能設置裏面填寫js接口安全域名,這個是要填寫的是你微信瀏覽器要打開的域名地址。不能添加IP地址。
設置時候點擊設置裏面有說明,如下圖
畫紅圈的要注意,下載完的那個文件放進去一定要在瀏覽器能訪問才能進行下一步。我是把這個文件放到tomcat下面的ROOT文件夾中,可以直接域名後面加文件名訪問。
網頁訪問的效果: - 在需要調用js接口的頁面接入JS文件 http://res.wx.qq.com/open/js/jweixin-1.4.0.js ,1.4.0是目前最新版本。網上有些地方用的是1.2.0.js,那個已經棄用了。
- 通過config接口注入權限並驗證配置
wx.config({ debug: true, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。 appId: '', // 必填,公衆號的唯一標識 timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的隨機串 signature: '',// 必填,簽名 jsApiList: [] // 必填,需要使用的JS接口列表 });
- 這一步算是整個步驟中最關鍵的一步,必須正確的配置信息纔可以進行調用JS-SDK。
獲取配置
獲取簽名實際是需要四步
1. 根據appId和appsecret獲取access_token;
2. 使用access_token獲取jsapi_ticket;
3. 使用時間戳,隨機數,jsapi_ticket和要訪問的url按照簽名算法拼接字符串;
4. 對第三步的字符串進行SHA1加密,得到簽名;
這步是在服務器後臺寫的一個工具類裏面實現的,測試時候可以先後臺獲取,前臺頁面將參數寫死,實際用的時候最好動態獲取這些數據。如果參數寫死的話會二次分享會出問題,在微信中第二次分享,微信會自己加一個參數上去,這樣點開在分享的時候簽名和url會匹配不上。
第一步,獲取access_token
appId和appsecret可以在微信公衆平臺--開發-基本配置中查找
/**
* 第一步,獲取access_token
* appId和appsecret可以在微信公衆平臺--開發-基本配置中查找
*/
public static String getAccess_token(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;
}
第二步 獲取jsapi_ticket
/**
* 第二步:獲得ACCESS_TICKET
*
* @Title: ACCESS_TICKET
* @Description: 獲得ACCESS_TICKET
* @param @return 設定文件
* @return String 返回類型
* @throws
*/
public static String getAccess_ticket(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;
}
這裏拿到對應的jsapi_ticket之後就可以進行參數排序和拼接字符串並加密
第三步:SHA1加密
/**
* 第三步: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 "";
}
加密算法轉載自:http://www.open-open.com/lib/view/open1392185662160.html
第四步: 獲取簽名
/**
* 第4步獲取簽名
* 公衆號
* String appId="wx86b6597*****";
* String appSecret="aeb428ffb9a8a071fc4******";
*/
public static void main(String[] args) {
String appId="wx118d752d*****"; //這裏寫自己的appId
String appSecret="947cce9d2808bca0976788d4*****";
//1、獲取AccessToken
String accessToken = getAccess_token(appId, appSecret);
//2、獲取Ticket
String jsapi_ticket = getAccess_ticket(accessToken);
//3、時間戳和隨機字符串
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//隨機字符串
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//時間戳
System.out.println("accessToken:"+accessToken+"\njsapi_ticket:"+jsapi_ticket+"\n時間戳:"+timestamp+"\n隨機字符串:"+noncestr);
//4、獲取url
String url="http://api.groupusonline.com/txrtWeb/test.html";
/*根據JSSDK上面的規則進行計算,這裏比較簡單,我就手動寫啦
String[] ArrTmp = {"jsapi_ticket","timestamp","nonce","url"};
Arrays.sort(ArrTmp);
StringBuffer sf = new StringBuffer();
for(int i=0;i<ArrTmp.length;i++){
sf.append(ArrTmp[i]);
}
*/
//5、將參數排序並拼接字符串
String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
//6、將字符串進行sha1加密
String signature =SHA1(str);
System.out.println("參數:"+str+"\n簽名:"+signature);
}
上面是工具類裏面的內容,接下來在控制類裏面寫調用接口。上面的這些內容就可以算出來簽名和一些配置需要的東西,測試時候可以直接算出來在html頁面裏面將參數寫死。
第五步:調用接口
/**
* 微信分享相關
* Created by yf on 2019/6/20.
*/
@Controller
@ResponseBody
@RequestMapping("wx")
public class WeChatHander {
private Logger log = LoggerFactory.getLogger(WeChatHander.class);
//公衆號信息
private static final String appId = "wx118d****12221";
private static final String appSecret="947cce9***88d4b898db3e";
//微信自動義分享
@RequestMapping("share")
public Object share(HttpSession session,String URL){
String url="";
//需要轉換解碼url
try {
url = java.net.URLDecoder.decode(URL,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//獲取access_token,這裏要從存到session,生成這個參數的wx調用接口有調用次數限制。這部分存session,下面的下載文件裏面沒有更新,自己添加進去吧,不然接口次數很快就用完了。
String aeecss_token=(String) session.getAttribute("aeecss_token");
if(aeecss_token==null){
aeecss_token = WeChatUtil.getAccess_token(appId, appSecret);
session.setAttribute("aeecss_token", aeecss_token);
}
//獲取access_ticket
String aeecss_ticket=(String) session.getAttribute("aeecss_ticket");
if(aeecss_ticket==null){
aeecss_ticket = WeChatUtil.getAccess_ticket(aeecss_token);
session.setAttribute("aeecss_ticket", aeecss_ticket);
}
//String aeecss_ticket = WeChatUtil.getAccess_ticket(aeecss_token);
//3、時間戳和隨機字符串
String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//隨機字符串
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//時間戳
System.out.println("accessToken:"+aeecss_token+"\njsapi_ticket:"+aeecss_ticket+"\n時間戳:"+timestamp+"\n隨機字符串:"+nonceStr);
//4、獲取url
//5、將參數排序並拼接字符串
String str = "jsapi_ticket="+aeecss_ticket+"&noncestr="+nonceStr+"×tamp="+timestamp+"&url="+url;
//6、將字符串進行sha1加密
String signature =WeChatUtil.SHA1(str);
System.out.println("參數:"+str+"\n簽名:"+signature);
Map<String,String> map=new HashMap();
map.put("appId",appId);
map.put("timestamp",timestamp);
map.put("accessToken",aeecss_token);
map.put("ticket",aeecss_ticket);
map.put("nonceStr",nonceStr);
map.put("signature",signature);
return map;
}
}
這樣通過這個接口獲取簽名後把所有的信息都返回json格式了。概要的都有了,這是就可以配置config了,此時html頁面中應該是:這是參數寫死的時候測試用。
<script type="text/javascript">
wx.config({
debug: true, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。
appId: 'wx118d75***212221', // 必填,公衆號的唯一標識
timestamp:'1560929330' , // 必填,生成簽名的時間戳
nonceStr: 'ab1ef978a35f4d4f', // 必填,生成簽名的隨機串
signature: '13159896a4f8f461e0f****9df4029818e6e',// 必填,簽名
jsApiList: [// 必填,需要使用的JS接口列表
'updateAppMessageShareData',
'updateTimelineShareData'
], // 需要檢測的JS接口列表,所有JS接口列表見附錄2,
});
//ready
wx.ready(function () { //需在用戶可能點擊分享按鈕前就先調用
shareData = {
title: "測試標題", // 分享標題
desc: "測試描述", // 分享描述
link: "http://****.com/txrtWeb/test.html", // 分享鏈接
imgUrl: "http://****.com/images/logo.png", // 分享圖標
success: function() {
// 設置成功
}
};
// 1.4.0 新接口 (只調用這個接口在安卓下是無效的)
wx.updateAppMessageShareData(shareData);
wx.updateTimelineShareData(shareData);
});
</script>
參數從接口獲取的時候如下:
<!-- Meta for OpenGraph -->
<meta name="title" content="測試頁面標題從meta獲取">
<meta name="description" content="描述">
<!-- End -->
<script src="include/jquery-1.7.1.min.js" language="JavaScript"></script>
<script src="http://res2.wx.qq.com/open/js/jweixin-1.4.0.js"language="JavaScript"></script> <!-- 微信js -->
<!--微信分享相關js -->
<script type="text/javascript">
var title=document.querySelector('meta[name="title"]').getAttribute('content'); //網頁標題
var desc=document.querySelector('meta[name="description"]').getAttribute('content'); //網頁描述
$.ajax({
async : true, //這裏參數true和false在微信中打開會有不同效果,實際用的時候用false,用true時候測試會有提示性內容。
url : 'http://****.com/**/wx/share',
//url : 'http://localhost:8081/txrt_api/wx/share',
type : "POST",
dataType : "json", // 返回的數據類型,設置爲JSONP方式
data : {
URL : encodeURIComponent(window.location.href.split("#")[0])
},
success: function(response, status, xhr){
//console.log('狀態爲:' + status + ',狀態是:' + xhr.statusText);
// alert("進來了");
wx.config({
debug: false, //調試階段建議開啓,關閉就不彈提示了
appId: response.appId,//APPID
timestamp: response.timestamp,//上面main方法中拿到的時間戳timestamp
nonceStr: response.nonceStr,//上面main方法中拿到的隨機數nonceStr
signature: response.signature,//上面main方法中拿到的簽名signature
//需要調用的方法接口
jsApiList: [
'updateAppMessageShareData','updateTimelineShareData']
});
//ready
wx.ready(function () { //需在用戶可能點擊分享按鈕前就先調用
shareData = {
title: title, // 分享標題
desc: desc, // 分享描述
link: window.location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公衆號JS安全域名一致
imgUrl: "http://api.groupusonline.com/txrtWeb/images/zfshlogo.png", // 分享圖標
success: function() {
//alert("設置成功分享");
// 設置成功
}
};
// 1.4.0 新接口 (只調用這個接口在安卓下是無效的)
wx.updateAppMessageShareData(shareData);
wx.updateTimelineShareData(shareData);
});
}
});
</script>
注意:
- url一定要是完整的url(當前網頁的URL,不包含#及其後面部分)最好是使用window.location.href.split("#")獲取得到,如果傳入固定的好像會報invalid signature錯誤,所以爲了安全起見還是直接傳入當前的url
- 這個簽名的有效時間爲7200秒,也就是2個小時,因此當超過兩個小時候,再訪問也會報invalid signature錯誤。但是這個我還沒有遇見,並不知道這麼解決,其他說是需要緩存access_token和access_ticket
- 另外還有一個錯誤:invalid url domain
這個跟生成簽名時用的url有關係,官網的說法是:
invalid url domain當前頁面所在域名與使用的appid沒有綁定,請確認正確填寫綁定的域名,如果使用了端口號,則配置的綁定域名也要加上端口號(一個appid可以綁定三個有效域名)
這個url必須是:“公衆號設置---功能設置----JS接口安全域名”中綁定的三個域名之一 - 如果以上配置都是正確,而且debug也設置爲true了,在微信中訪問連接會出現config:ok的界面,就說明配置成功了
- 有時也可以把ajax請求放在setTimeout中進行請求。
驗證工具:
微信公衆平臺接口調試工具
微信 JS 接口簽名校驗工具
參考:
解決微信JS-SDK掃一掃功能接入以及出現簽名無效 invalid signature
微信JS-SDK獲取signature簽名以及config配置
相關代碼文件下載:https://download.csdn.net/download/qq_37698433/11253616
求打賞