根據開發文檔,首先看一下前端需要什麼資料:
var currentUrl = window.location.href;
//微信分享功能實作
$.ajax({//調取接口傳回驗證碼
type:"get",
url:"http://域名/接口名",
data:{'url':currentUrl},
success:function(res){
console.log(res)
if(res.success==true){
//分享連結
var imgUrl = '圖檔位址';
var lineLink = 'http://域名/檔案名/';
var descContent = "分享展示内容";
var shareTitle = '分享标題';
wx.config({
debug: false, // 開啟調試模式,調用的所有api的傳回值會在用戶端alert出來,若要檢視傳入的參數,可以在pc端打開,參數資訊會通過log打出,僅在pc端時才會列印。
appId: 'wxdbcbd7962d016d0b', // 必填,公衆号的唯一辨別
timestamp: res.data.timestamp, // 必填,生成簽名的時間戳
nonceStr: res.data.nonceStr, // 必填,生成簽名的随機串
signature: res.data.signature,// 必填,簽名,見附錄1
jsApiList: [
'checkJsApi',
'onMenuShareAppMessage',
'onMenuShareTimeline',
'onMenuShareQQ'
] // 必填,需要使用的JS接口清單,所有JS接口清單見附錄2
});
wx.ready(function(){
// config資訊驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個用戶端的異步操作,是以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確定正确執行。對于使用者觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
wx.checkJsApi({
jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ'], // 需要檢測的JS接口清單,所有JS接口清單見附錄2,
success: function(res) {
// console.log(res);
// 以鍵值對的形式傳回,可用的api值true,不可用為false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
}
});
wx.onMenuShareAppMessage({
title: shareTitle, // 分享标題
desc: descContent, // 分享描述
link: lineLink, // 分享連結,該連結域名或路徑必須與目前頁面對應的公衆号JS安全域名一緻
imgUrl: imgUrl, // 分享圖示
type: '', // 分享類型,music、video或link,不填預設為link
dataUrl: '', // 如果type是music或video,則要提供資料連結,預設為空
success: function () {
// 使用者确認分享後執行的回調函數
//alert('success')
},
cancel: function () {
// 使用者取消分享後執行的回調函數
// alert('false');
}
});
wx.onMenuShareTimeline({
title: shareTitle, // 分享标題
link: lineLink, // 分享連結,該連結域名或路徑必須與目前頁面對應的公衆号JS安全域名一緻
imgUrl: imgUrl, // 分享圖示
success: function () {
// 使用者确認分享後執行的回調函數
// alert('success')
},
cancel: function () {
// 使用者取消分享後執行的回調函數
// alert('false');
}
});
wx.onMenuShareQQ({
title: shareTitle, // 分享标題
desc: descContent, // 分享描述
link: lineLink, // 分享連結
imgUrl: imgUrl, // 分享圖示
success: function () {
// 使用者确認分享後執行的回調函數
},
cancel: function () {
// 使用者取消分享後執行的回調函數
}
});
});
wx.error(function(res){
// config資訊驗證失敗會執行error函數,如簽名過期導緻驗證失敗,具體錯誤資訊可以打開config的debug模式檢視,也可以在傳回的res參數中檢視,對于SPA可以在這裡更新簽名。
//alert(res);
return;
});
}
else {
// alert(res.message)
return
}
},
error:function(){
console.log("error");
}
});
通過上面的js可以看到背景需要提供三個參數
一、timestamp
根據文檔得知,要想得到timestamp需要得到jsapi_ticket,而要想得到jsapi_ticket需要得到aaccess_token。
1、擷取access_token
access_token有效期為7200秒,重複擷取将導緻上次擷取的access_token失效,是以問題是如何定時擷取并且把得到的資料放在本地。
1)建立一個AccessToken實體類
public class AccessToken {
private String accessToken;
private int expiresin;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresin() {
return expiresin;
}
public void setExpiresin(int expiresin) {
this.expiresin = expiresin;
}
}
2)寫一個servlet定時擷取access_token,并配置在web.xml中
AccessTokenServlet:
@WebServlet(name = "accessTokenServlet")
public class AccessTokenServlet extends HttpServlet{
private static final long serialVersionUID = L;
private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);
@Override
public void init() throws ServletException {
TokenThread.appId = getInitParameter("appid"); //擷取servlet初始參數appid和appsecret
TokenThread.appSecret = getInitParameter("appsecret");
log.info("weixin api appid:{}", TokenThread.appId);
log.info("weixin api appsecret:{}", TokenThread.appSecret);
if ("".equals(TokenThread.appId) || "".equals(TokenThread.appSecret)) {
log.error("appid and appsecret configuration error, please check carefully.");
} else {
// 啟動定時擷取access_token的線程
new Thread(new TokenThread()).start();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
web.xml配置
<servlet>
<servlet-name>accessTokenServlet</servlet-name>
<servlet-class>com.loan.servlet.AccessTokenServlet</servlet-class>
<init-param>
<param-name>appid</param-name>
<param-value>你的appid</param-value>
</init-param>
<init-param>
<param-name>appsecret</param-name>
<param-value>你的appsecret</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
3)TokenThread類
public class TokenThread implements Runnable {
private static Logger log = LoggerFactory.getLogger(TokenThread.class);
public static String appId = "";
public static String appSecret= "";
public static AccessToken accessToken = null;
@Override
public void run() {
while (true) {
try {
accessToken = WxCommonUtil.getToken(appId,appSecret);
if (null != accessToken) {
System.out.println(accessToken.getAccessToken());
Thread.sleep( * ); //擷取到access_token 休眠7000秒
} else {
Thread.sleep( * ); //擷取的access_token為空 休眠3秒
}
} catch (Exception e) {
System.out.println("發生異常:" + e.getMessage());
e.printStackTrace();
try {
Thread.sleep( * ); //發生異常休眠1秒
} catch (Exception e1) {
}
}
}
}
}
4)WxCommonUtil工具類
public class WxCommonUtil {
private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);
public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=你的appid&secret=你的secret";
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
try {
// 建立SSLContext對象,并使用我們指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 從上述SSLContext對象中得到SSLSocketFactory對象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 設定請求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 當outputStr不為null時向輸出流寫資料
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意編碼格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 從輸入流讀取傳回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 釋放資源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
log.error("連接配接逾時:{}", ce);
} catch (Exception e) {
log.error("https請求異常:{}", e);
}
return jsonObject;
}
這樣accessToken可以通過TokenThread.accessToken.getAccessToken()擷取。
2、擷取jsapi_ticket
/**
* 擷取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;
}
3、擷取url
//這裡URL要從前端動态擷取,不能寫死
String url=request.getParameter("url");
4、拼接字元串并進行sha1加密
String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
/**
* sha1的加密算法
*
* @param decript
* @return
*/
public static String SHA1(String decript) {
String signature="";
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(decript.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
return signature;
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return "";
}
/**
* 進制轉換
* @param hash
* @return
*/
public 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;
}
這樣一系列步驟之後就可以得到signature。
二、timestamp
String timestamp = String.valueOf(System.currentTimeMillis() / );
三、nonceStr
這樣就能得到三個參數啦。
最後是總的controller:
@RequestMapping(value = "/sign",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
@ResponseBody
public ObjectOutput sign(HttpServletRequest request, HttpServletResponse response, HttpSession session){
Hashtable hashtable = new Hashtable();
//、擷取accessToken
String accessToken= TokenThread.accessToken.getAccessToken();
//、擷取Ticket
String jsapi_ticket=WxCommonUtil.getTicket(accessToken);
//、時間戳和随機字元串
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(, );
String timestamp = String.valueOf(System.currentTimeMillis() / );
//、擷取url
String url=request.getParameter("url");
//、講參數排序并拼接字元串
String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
//、将字元串進行sha1加密
String signature =WxCommonUtil.SHA1(str);
hashtable.put("signature",signature);
hashtable.put("timestamp",timestamp);
hashtable.put("nonceStr",noncestr);
hashtable.put("url",url);
output = Format.output(true, Constant.Success, hashtable, "");
return output;
}