微信內部分享-H5頁面-JS+java程式碼
app內部分享到微信,QQ都正常
再從微信開啟,繼續分享給朋友或者朋友圈,就變成下面這個樣子了
一切變得那麼的。。。無助!!!
開始以為在H5頁面上新增一些東西即可,後來發現,完全不是自己想象的那個樣子。
這個東西,對於一個從未用過微信JS的碼農來說,或許要被帶坑裡去卡個幾天!!!
以下是本人小菜的一點點經驗拿來和各位分享,希望剛接觸到的少走一些彎路!
微信公眾賬號平臺有提供一個js-sdk開發文件,有一定的開發規範,對又到分享等違規行為也有嚴格要求。按著開發文件做,區別就是通過後臺驗證一下APPID與SECRET是否正確,是否授權等,噁心之處就在後臺驗證。
首先,和微信公眾號開發一樣,首先必須做好最前面5個步驟,第三部最重要!!!
才可以使用JS呼叫微信介面,呼叫之前的驗證,對一個微信小白來講,是一個很繁瑣的過程
我這邊就先上JS和JAVA程式碼吧,現在講公眾號配置估計都是一臉懵逼
第一步:
1.在需要呼叫JS介面的頁面引入如下JS檔案
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
需要分享的H5頁面加上以下程式碼
<%--------------------------------------- 微信分享開始 ---------------------%> <script> // 只有先這樣取值,再賦值給下面的title,desc,imgUrl,link,不然IOS蘋果端無法呼叫成功 var shareTitle = '${shareBean.shareTitle}'; var shareContent = '${shareBean.shareContent}'; var shareImage = '${shareBean.shareImage}'; // 分享連結必須用這個,別問為什麼,微信就是坑 var shareUrl = location.href.split('#')[0].toString(); var title = shareTitle; var desc = shareContent; var imgUrl = shareImage; // 分享的圖片,最好是正方形,不是也沒關係,但是一定是http模式,即絕對路徑,而不是伺服器路勁 var link = shareUrl; // 該連結域名或路徑必須與當前頁面對應的公眾號JS安全域名一致-----這個特別重要,請看清楚,這裡的地址可以寫死,也可以動態獲取,但是一定不能帶有微信分享後的引數,不然分享也是失敗的 </script> <c:import url="../wechat_share.jsp"/>----這個頁面寫公共的JS方法,在下面 <%--------------------------------------- 微信分享結束 ----------------------%>
<script> // 當前頁面訪問路徑 var url = location.href.split('#')[0].toString(); var getUrl = "wechat/getsignature"; // ajax請求路徑 $.get(getUrl, {"url": url}).done(function (data) { // 注意這裡的url,一定要這樣寫,也就是動態獲取,千萬不要寫死,不然也不會成功的。連結字首要和安全域名一致 // console.log(data); // console.log(data.code); if (data.code == 1) { var wxInfo = data.wxInfo; if (wxInfo.signature != null) { wx.config({ debug: false, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。 appId: wxInfo.appId, // 必填,公眾號的唯一標識 timestamp: wxInfo.timestamp, // 必填,生成簽名的時間戳 nonceStr: wxInfo.nonceStr, // 必填,生成簽名的隨機串 signature: wxInfo.signature,// 必填,簽名,見附錄1 jsApiList: [ 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone' ] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2 }); } } }).fail(function (msg) { // console.log("error:" + msg); }); // 分享給朋友、QQ、微博 var shareData = { "imgUrl": imgUrl, "title": title, "desc": desc, 'link': link, success: function() { // layer.msg("分享成功~", {}); }, cancel: function() { // alert("取消分享"); } }; // 分享到朋友圈 var shareToTimeline = { "imgUrl": imgUrl, "title": title, 'link': link, success: function() { // layer.msg("分享成功~", {}); }, cancel: function() { // alert("取消分享"); } } wx.ready(function () { wx.onMenuShareTimeline(shareToTimeline); // 分享到微信朋友圈 wx.onMenuShareAppMessage(shareData); // 分享給微信朋友 wx.onMenuShareQQ(shareData); // 分享到QQ wx.onMenuShareQZone(shareData); // 分享到QQ空間 wx.onMenuShareWeibo(shareData); // 分享到微博 wx.error(function (res) { alert(res.errMsg); }); }); </script>
<script>
// 當前頁面訪問路徑
var url = location.href.split('#')[0].toString();
var getUrl = "wechat/getsignature"; // ajax請求路徑
$.get(getUrl,
{"url": url}).done(function (data) {
// 注意這裡的url,一定要這樣寫,也就是動態獲取,千萬不要寫死,不然也不會成功的。連結字首要和安全域名一致
// console.log(data);
// console.log(data.code);
if (data.code == 1) {
var wxInfo = data.wxInfo;
if (wxInfo.signature != null) {
wx.config({
debug: false, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。
appId: wxInfo.appId, // 必填,公眾號的唯一標識
timestamp: wxInfo.timestamp, // 必填,生成簽名的時間戳
nonceStr: wxInfo.nonceStr, // 必填,生成簽名的隨機串
signature: wxInfo.signature,// 必填,簽名,見附錄1
jsApiList: [
'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'
] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
});
}
}
}).fail(function (msg) {
// console.log("error:" + msg);
});
// 分享給朋友、QQ、微博
var shareData = {
"imgUrl": imgUrl,
"title": title,
"desc": desc,
'link': link,
success: function() {
// layer.msg("分享成功~", {});
},
cancel: function() {
// alert("取消分享");
}
};
// 分享到朋友圈
var shareToTimeline = {
"imgUrl": imgUrl,
"title": title,
'link': link,
success: function() {
// layer.msg("分享成功~", {});
},
cancel: function() {
// alert("取消分享");
}
}
wx.ready(function () {
wx.onMenuShareTimeline(shareToTimeline); // 分享到微信朋友圈
wx.onMenuShareAppMessage(shareData); // 分享給微信朋友
wx.onMenuShareQQ(shareData); // 分享到QQ
wx.onMenuShareQZone(shareData); // 分享到QQ空間
wx.onMenuShareWeibo(shareData); // 分享到微博
wx.error(function (res) {
alert(res.errMsg);
});
});
</script>
第二步:
先登入微信公眾號平臺
ajax請求後臺java介面
/**
* 用途描述:微信內部點選分享Controller
*
* @author
* @version 1.0.0
* @date 2018年2月13日 上午9:38:42
*/
@RestController
@RequestMapping(value = "/wechat")
public class WeChatShareController extends BaseController {
@Autowired
private WeChatShareService weChatShareService;
/**
* 獲取微信加密資訊
*
* @param request
* @return
*/
@RequestMapping(value = "/getsignature")
public IData toTranscript(HttpServletRequest request) {
IData jdata = new DataMap(); // 這是我們公司自己封裝的類,知道意思就行
Locale locale = RequestContextUtils.getLocaleResolver(request).resolveLocale(request);
try {
// 獲取url
String url = this.objToString("url", "", request);
Map<String, Object> wxInfo = weChatShareService
.queryWechatInfo(url);
IData code = this.sendPromptMsg("1", "獲取成功"); // 查詢成功!
jdata.putAll(code);
jdata.put("wxInfo", wxInfo);
return jdata;
} catch (Exception e) {
e.printStackTrace();
return this.sendPromptMsg("1000", locale); // 未知錯誤
}
}
}
/**
* 用途描述:微信內部點選分享Service
*
* @author
* @version 1.0.0
* @date 2018年2月13日 上午9:38:42
*/
@Service
public class WeChatShareService {
@Autowired
private WechatShareTokenDao wechatShareTokenDao;
@Autowired
private WechatShareTokenDao reckonShareTokenDao;
/**
* 獲取微信加密資訊
*
* @param url
* @return
* @throws Exception
*/
public Map<String, Object> queryWechatInfo(String url) throws Exception {
Map<String, Object> wxInfo = new HashMap<>();
// 這裡的兩個加密資訊,是我通過一個服務程式,開啟執行緒,每隔30分鐘獲取一次,並存儲到資料庫中,因為微信公眾號獲取次數有限,而且兩個都是2小時失效
String accessToken = "";
String jsapiTicket = "";
//1、獲取AccessToken和jsapiTicket,先從資料庫獲取最近一次儲存的。
Map<String, Object> result = wechatShareTokenDao.queryWechatInfo();
if (null != result && !result.isEmpty()) {
accessToken = CommonUtil.objToString(result.get("wechat_access_token"), "");
jsapiTicket = CommonUtil.objToString(result.get("wechat_jsapi_ticket"), "");
}
//3、時間戳和隨機字串
long currentTimes = System.currentTimeMillis(); // 時間戳
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//隨機字串
String timestamp = String.valueOf(currentTimes / 1000);// 時間戳
//5、將引數排序並拼接字串
String params = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;
//6、將字串進行sha1加密
String signature = Constant.getSHA1(params);
//7、微信appId
String appId = WeChatUitl.getAppIdWx();
wxInfo.put("appId", appId);
wxInfo.put("accessToken", accessToken);
wxInfo.put("jsapiTicket", jsapiTicket);
wxInfo.put("timestamp", timestamp);
wxInfo.put("nonceStr", noncestr);
wxInfo.put("params", params);
wxInfo.put("signature", signature);
return wxInfo;
}
}
/**
* SHA、SHA1加密
*
* @parameter: str:待加密字串
* @return: 加密串
**/
public static String getSHA1(String str) {
try {
MessageDigest digest = java.security.MessageDigest
.getInstance("SHA-1"); //如果是SHA加密只需要將"SHA-1"改成"SHA"即可
digest.update(str.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexStr = new StringBuffer();
// 位元組陣列轉換為 十六進位制 數
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}
return hexStr.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* 用途描述: 微信開發獲取資訊---微信開放平臺
*
* @author
* @version 1.0.0
* @date 2018-02-13
*/
public class WeChatUitl {
// 微信appid---微信公眾平臺
private static String appIdWx = "aaaaaaaaaaaaaaa";
// 微信AppSecret---微信公眾平臺
private static String appSecretWx = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
private static String keyWx = "ccccccccccccccccccccccccc"; // 微信key 應用簽名
public static String getAppIdWx() {
return appIdWx;
}
WeChatUitl.appIdWx = appIdWx;
}
public static String getAppSecretWx() {
return appSecretWx;
}
public static void setAppSecretWx(String appSecretWx) {
WeChatUitl.appSecretWx = appSecretWx;
}
public static String getKeyWx() {
return keyWx;
}
public static void setKeyWx(String keyWx) {
WeChatUitl.keyWx = keyWx;
}
/**
* 獲取access_token
*
* @return
*/
public static String getAccessToken() {
String access_token = "";
String grant_type = "client_credential";//獲取access_token填寫client_credential
String AppId = appIdWx;//第三方使用者唯一憑證
String secret = appSecretWx;//第三方使用者唯一憑證金鑰,即appsecret
//這個url連結地址和引數皆不能變
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type + "&appid=" + AppId + "&secret=" + secret;
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);
access_token = demoJson.getString("access_token");
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return access_token;
}
/**
* 獲取jsapi_ticket
*
* @param access_token
* @return
*/
public static String getTicket(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;
}
public static void main(String[] args) {
//1、獲取AccessToken
String accessToken = getAccessToken();
// String accessToken = "6_Exc9VRFdPMLeF-4gPaJJGmoo-BJUGzgSJcs3vkT_y4eXPiQzRf1vdMvlVXNE85sfYH9AtQcdd-zptyD5t5S98VXSwIapyMoYjBNfvH7A11GZOoWs2u6agFlLS9NMqzTgN1N5V16BZrL1BnV_WTIaAIAHET";
//2、獲取Ticket
String jsapi_ticket = getTicket(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://www.luiyang.com/add.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 = Constant.getSHA1(str);
System.out.println("引數:" + str + "\n簽名:" + signature);
}
}
確認簽名演算法是否正確
可用此頁面工具進行校驗
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
自己寫一個定時任務程式,去獲取AccessToken,jsapi_ticket ,並存儲資料庫,加上redis快取,每次更新、讀取快取就可以了。
這樣就避免使用者每次分享,都要去呼叫介面獲取簽名,畢竟次數有限!!!
推薦用 spring security,簡單,方便,實用
第三步:
接下來最重要的步驟:
1,最好先去申請一個線上域名(花生殼),用於測試,不然只有釋出到線上測試了
2,在公總號後臺新增IP白名單
本機IP獲取方式
本地測試的時候ip不是這這樣查詢,具體怎麼獲取,emmmmm 我忘了
反正到時候呼叫“獲取access_token”介面,返回結果。
如非白名單IP呼叫,將返回錯誤碼:40164,日誌會列印錯誤資訊,xxx.xx.xx.xx 的ip不安全,就是這個。
在這裡也一起把線上伺服器的IP也一起設定進去,因為每次設定的時候都要找老闆的手機來掃微信二維碼,腦殼疼
注意,如果線上伺服器ip地址有變動,必須修改微信公眾號ip白名單配置
第四步:
配置公眾號業務域名和js安全域名
按照步驟下載檔案,將檔案複製到專案根目錄和tomcat專案根目錄
我這裡設定的域名,跟安卓,ios商量好了,叫他們設定介面訪問的字首要包含專案名!!!
比如專案名是:tb_project
本地測試呼叫:localhost:8080/taobao_project/querylist
花生殼呼叫介面方式:xxx.xicp.io/querylist
到這裡,就完全到位了,over