微信公衆號開發,在公衆號界面調用微信JS SDK接口,實現媒體(圖片、語言、視頻)上傳,分析等功能之前,需要獲取Config接口注入權限驗證。
微信Config接口注入權限驗證,需要如下參數:
wx.config({
debug: true, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。
appId: '', // 必填,公衆號的唯一標識
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名
jsApiList: [] // 必填,需要使用的JS接口列表
});
那麼如何獲得Config接口注入權限驗證的相關參數:其中appId,timestamp,nonceStr是自己後臺隨機生成的,但是 signatrue簽名生成需要jsapi_ticket,並按照指定順序將拼接好的參數字符串進行加密,生成簽名,下面首先了解如何生成jsapi_ticket:
jsapi_ticket是公衆號用於調用微信JS接口的臨時票據。正常情況下,jsapi_ticket的有效期爲7200秒,通過access_token來獲取。
因爲Access_token,Jsapi_ticket是有次數限制的,所以需要將數據生成並保持在緩存中,或者數據庫中,過期之後,重新拉取。
下面給出Demo示例,僅供參考:
package com.inspur.tax.api.login.controller;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.SignUtils;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.alibaba.fastjson.JSONObject;
import com.inspur.tax.api.login.entity.LoginUser;
import com.inspur.tax.api.login.service.ILoginUserService;
import com.inspur.tax.api.sms.cache.CacheManagerImpl;
import com.inspur.tax.api.util.WeixinUtil;
import com.inspur.tax.api.common.Const;
import com.inspur.tax.utils.DateUtils;
import com.inspur.tax.utils.FastJsonUtils;
import com.inspur.tax.utils.JwtUtil;
import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/WX/JsapiTicket")
public class WxJsapiTicketController {
private final WxMpService wxService;
@Autowired
private ILoginUserService loginUserService;
static CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();
@RequestMapping("/wxConfig/{appid}")
public Map<String,Object> index(@PathVariable String appid,@RequestParam String urlx) {
Map<String,Object> wxConfig = new HashMap<String,Object>();
try {
/**
* 生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公衆號用於調用微信JS接口的臨時票據。正常情況下,
* jsapi_ticket的有效期爲7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api調用次數非常有限,
* 頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket 。
參考以下文檔獲取access_token(有效期7200秒,開發者必須在自己的服務全局緩存access_token):
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
用第一步拿到的access_token 採用http GET方式請求獲得jsapi_ticket(有效期7200秒,開發者必須在自己的服務全局緩存jsapi_ticket):
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
* */
//access_token 和jsapi_token 都是需要本地緩存的
//通過appid獲得secret
String secret = Const.SERVICE_WxConfig.get(appid);
//判斷是否在緩存中
String token2 = "";
boolean flagTimeOut = false;
boolean flagExist = false;
flagExist = cacheManagerImpl.isContains(appid+"AC");
if(flagExist) {
//判斷是否過期
flagTimeOut = cacheManagerImpl.isTimeOut(appid+"AC");
if(flagTimeOut) {
cacheManagerImpl.clearByKey(appid+"AC");
}
}
if(!flagTimeOut && flagExist) {
token2 = cacheManagerImpl.getCacheByKey(appid+"AC").getDatas().toString();
}else {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appid + "&secret=" + secret;
JSONObject json = WeixinUtil.httpRequest(url, "GET", null);
token2=json.getString("access_token");
cacheManagerImpl.putCache(appid+"AC", token2, 6500*1000);
}
String jsapi_token = "";
flagExist = cacheManagerImpl.isContains(appid+"JT");
if(flagExist) {
//判斷是否過期
flagTimeOut = cacheManagerImpl.isTimeOut(appid+"JT");
if(flagTimeOut) {
cacheManagerImpl.clearByKey(appid+"JT");
}
}
if(!flagTimeOut && flagExist) {
jsapi_token = cacheManagerImpl.getCacheByKey(appid+"JT").getDatas().toString();
}else {
String jsapiUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ token2 + "&type=jsapi";
JSONObject jsapiJson = WeixinUtil.httpRequest(jsapiUrl, "GET", null);
if (jsapiJson != null) {
jsapi_token = jsapiJson.getString("ticket");
}
cacheManagerImpl.putCache(appid+"JT", jsapi_token, 6500*1000);
}
wxConfig = WeixinUtil.getWxConfig(urlx, jsapi_token,appid);
} catch (Exception e) {
log.error("JsapiTicket,異常日誌",e);
}
return wxConfig;
}
}
package com.inspur.tax.api.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.alibaba.fastjson.JSONObject;
public class WeixinUtil {
/**
* 方法名:httpRequest</br>
* 詳述:發送http請求</br>
* 開發人員:souvc </br>
* 創建時間:2016-1-5 </br>
* @param requestUrl
* @param requestMethod
* @param outputStr
* @return 說明返回值含義
* @throws 說明發生此異常的條件
*/
public static JSONObject httpRequest(String requestUrl,String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
ce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return jsonObject;
}
/**
* 方法名:getWxConfig</br>
* 詳述:獲取微信的配置信息 </br>
* 開發人員:souvc </br>
* 創建時間:2016-1-5 </br>
* @param request
* @return 說明返回值含義
* @throws 說明發生此異常的條件
*/
public static Map<String, Object> getWxConfig(String urlx,String jsapi,String appid) {
Map<String, Object> ret = new HashMap<String, Object>();
//HttpSession session=request.getSession();
String appId = appid; // 必填,公衆號的唯一標識
String secret = "";
String requestUrl = urlx;
String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成簽名的時間戳
String nonceStr = UUID.randomUUID().toString(); // 必填,生成簽名的隨機串
String signature = "";
// 注意這裏參數名必須全部小寫,且必須有序
System.out.println("jsapi_ticket:"+jsapi);
String sign = "jsapi_ticket=" + jsapi + "&noncestr=" + nonceStr+ "×tamp=" + timestamp + "&url=" + requestUrl;
try {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(sign.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
ret.put("appId", appId);
ret.put("timestamp", timestamp);
ret.put("nonceStr", nonceStr);
ret.put("signature", signature);
return ret;
}
/**
* 方法名:byteToHex</br>
* 詳述:字符串加密輔助方法 </br>
* 開發人員:souvc </br>
* 創建時間:2016-1-5 </br>
* @param hash
* @return 說明返回值含義
* @throws 說明發生此異常的條件
*/
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;
}
}
通過上面Demo,我們可以獲取微信Config接口注入權限驗證所需要的參數;然後就可以按照以下操作,調用微信JSSDK接口
JSSDK使用步驟
步驟一:綁定域名
先登錄微信公衆平臺進入“公衆號設置”的“功能設置”裏填寫“JS接口安全域名”。
備註:登錄後可在“開發者中心”查看對應的接口權限。
步驟二:引入JS文件
在需要調用JS接口的頁面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js
如需進一步提升服務穩定性,當上述資源不可訪問時,可改訪問:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支持https)。
備註:支持使用 AMD/CMD 標準模塊加載方法加載
步驟三:通過config接口注入權限驗證配置
所有需要使用JS-SDK的頁面必須先注入配置信息,否則將無法調用(同一個url僅需調用一次,對於變化url的SPA的web app可在每次url變化時進行調用,目前Android微信客戶端不支持pushState的H5新特性,所以使用pushState來實現web app的頁面會導致簽名失敗,此問題會在Android6.2中修復)。
wx.config({
debug: true, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。
appId: '', // 必填,公衆號的唯一標識
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名
jsApiList: [] // 必填,需要使用的JS接口列表
});
步驟四:通過ready接口處理成功驗證
wx.ready(function(){
// config信息驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
});
步驟五:通過error接口處理失敗驗證
wx.error(function(res){
// config信息驗證失敗會執行error函數,如簽名過期導致驗證失敗,具體錯誤信息可以打開config的debug模式查看,也可以在返回的res參數中查看,對於SPA可以在這裏更新簽名。
});
其他方法請參考官方文檔:
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
參考文檔:
https://www.cnblogs.com/haw2106/p/10345081.html
關注公衆號的都知道: