一、ws.config簽名 調用ticket等獲取ws.config的簽名,下面會調用方法再調用方法時須要再次按照調用方法的簽名javascript
wx.config({
debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。
appId: '', // 必填,公衆號的惟一標識
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名
jsApiList: [] // 必填,須要使用的JS接口列表
});php
jsapi_tickethtml
生成簽名以前必須先了解一下jsapi_ticket,jsapi_ticket是公衆號用於調用微信JS接口的臨時票據。正常狀況下,jsapi_ticket的有效期爲7200秒,經過access_token來獲取。因爲獲取jsapi_ticket的api調用次數很是有限,頻繁刷新jsapi_ticket會致使api調用受限,影響自身業務,開發者必須在本身的服務全局緩存jsapi_ticket 。java
一、獲取access_token(有效期7200秒,開發者必須在本身的服務全局緩存access_token):
http請求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=appID&secret=appsecretnode
二、用第一步拿到的access_token 採用http GET方式請求得到jsapi_ticket(有效期7200秒,api 調用次數很是有限,頻繁刷新api_ticket 會致使api調用受限,影響自身業務,開發者必須在本身的服務全局緩存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapijquery
成功返回以下JSON:git
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
1
2
3
4
5
6web
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=27_1JoR00pdKD26t0IbR_kPzC5FbCChBLVmoRTkJPQ6b3SbHO2D-IfeaCe1-iBI-kFCjZ58QCSffv9IEVhv0PfmfCsT4ZAEDNcwfO8zYEtB05SOM-mY8pspfKJsz_V8LJnqhMWJO-R9ymZhBj00UWRdACAIKF&type=jsapiajax
得到jsapi_ticket以後,就能夠生成JS-SDK權限驗證的簽名了。
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx954f1899ef15d2ea&secret=9af198bddb8db015e9113ed7379cbcdfapache
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=20_
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://res.wx.qq.com/open/js/jweixin-1.5.0.js"></script> <script src="jquery-3.4.1.min.js"></script> <script src="vconsole.min.js"></script> <script type="text/javascript"> var vConsole = new VConsole(); window.vConsole = new window.VConsole(); var appidG = ""; var timestampG = ""; var nonceStrG = ""; var signatureG = ""; $(function () { var signUrl = window.location.href.split('#')[0]; console.log("signUrl:" + signUrl); $.ajax({ url: "/index/test", method: "post", data: { signUrl: signUrl }, success: function (data) { console.log("data:" + data); var dataJson = JSON.parse(data); // console.log("dataJson.appid:"+dataJson.appid); console.log("wx.config() ---> 接收後臺返回的參數"); appidG = dataJson.appid; timestampG = dataJson.timestamp; nonceStrG = dataJson.nonceStr; signatureG = dataJson.signature; wx.config({ debug: true, appId: dataJson.appid, timestamp: dataJson.timestamp, nonceStr: dataJson.nonceStr, signature: dataJson.signature, jsApiList: ['onMenuShareAppMessage', 'openBusinessView'] }) } }); }); function getLocation() { wx.ready(function () { wx.getLocation({ type: 'gcj02', // 默認爲wgs84的gps座標,若是要返回直接給openLocation用的火星座標,可傳入'gcj02' success: function (res) { var latitude = res.latitude; // 緯度,浮點數,範圍爲90 ~ -90 var longitude = res.longitude; // 經度,浮點數,範圍爲180 ~ -180。 var speed = res.speed; // 速度,以米/每秒計 var accuracy = res.accuracy; // 位置精度 alert('緯度:' + latitude + '------經度:' + longitude) }, fail: function (res) { console.log('未開啓定位功能'); }, cancel: function (res) { console.log('用戶拒絕受權獲取地理位置'); } }); }); } /** * 跳轉微信支付分 */ function goToWXScore() { console.log("appidG:" + appidG); console.log("timestampG:" + timestampG); console.log("nonceStrG:" + nonceStrG); console.log("signatureG:" + signatureG); $.ajax({ url: "/index/generateSignature", method: "post", data: { timestamp: timestampG, nonce_str:nonceStrG }, success: function (sign) { console.log("sign:" + sign); wx.ready(function () { wx.checkJsApi({ jsApiList: ['openBusinessView'], // 須要檢測的JS接口列表 success: function (res) { // 以鍵值對的形式返回,可用的api值true,不可用爲false // 如:{"checkResult":{"openBusinessView":true},"errMsg":"checkJsApi:ok"} if (res.checkResult.openBusinessView) { wx.invoke( 'openBusinessView', { businessType: 'wxpayScoreEnable', queryString: "mch_id=1518750531&service_id=00004000000000704283351234894845&out_request_no=1234323JKHDFE1243259×tamp=" + timestampG + "" + "&nonce_str=" + nonceStrG + "&sign_type=HMAC-SHA256&sign=" + sign + "" }, function (res) { // 從微信側小程序返回時會執行這個回調函數 if (parseInt(res.err_code) === 0) { s // 返回成功 } else { // 返回失敗 } }); } } }); }); } }); } </script> </head> <body> <div> <!--<button id="snap1" onclick="goToWXScore()">goToWXScore</button>--> <button id="pay" onclick="goToWXScore()" value="跳轉支付分">跳轉支付分</button> <button id="snap2" onclick="getLocation()" value="獲取地區">獲取地區</button> <!--<button id="snap3" onclick="onBridgeReady()">支付</button>--> </div> </body> </html>
後端接口
@ResponseBody @RequestMapping("/test") @Login(required = false) public String test(String signUrl) { System.out.println("signUrl:"+signUrl); //String res = HttpGetMethod.doGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx954f1899ef15d2ea&secret=acb8ad2a46765766c7881ea37237c1c7", "utf8", null); String jsapi_ticket = "HoagFKDcsGMVCIY2vOjf9kjcHCRqLoF8k77O2dyyZiMTaOwxLTQ9nHU3fa4jfiEj2XW4NsvQG53gZZgVMkQdJA"; //String url = "http://localhost/h5/weixinexer.html"; Map<String, String> ret = com.jd.mrd.m.nc.web.utils.Sign.sign(jsapi_ticket, signUrl); ret.put("appid","wx954f1899ef15d2ea"); for (Map.Entry entry : ret.entrySet()) { System.out.println(entry.getKey() + ", " + entry.getValue()); } return JSON.toJSONString(ret); } /** * 獲取簽名 * @param timestamp * @param nonce_str * @return * @throws Exception */ @ResponseBody @RequestMapping("/generateSignature") @Login(required = false) public String generateSignature(String timestamp,String nonce_str) throws Exception { HashMap<String, String> stringStringHashMap = new HashMap<String, String>(); stringStringHashMap.put("mch_id","1518750531"); stringStringHashMap.put("service_id","00004000000000704283351234894845"); stringStringHashMap.put("out_request_no","1234323JKHDFE1243259"); stringStringHashMap.put("timestamp",timestamp); stringStringHashMap.put("nonce_str",nonce_str); stringStringHashMap.put("sign_type","HMAC-SHA256"); String signature = WeChatUtils.generateSignature(stringStringHashMap, "acb8ad2a46765766c7881ea37237c1c7", WeChatConstant.SignType.HMACSHA256); return signature; } @RequestMapping(value = "generateImage") @ResponseBody @Login(required = false) public String generateImage(HttpServletRequest request, String base64) throws IOException { //String base64=request.getParameter("base64");; File directory = new File("");//設定爲當前文件夾 URL resource = IndexController.class.getResource("/"); String clasFilePath = resource.getPath(); System.out.println(clasFilePath);//獲取標準的路徑 File file = new File(clasFilePath); String strParentDirectory = file.getParent(); System.out.println(strParentDirectory); file = new File(strParentDirectory); strParentDirectory = file.getParent(); System.out.println(strParentDirectory); strParentDirectory = strParentDirectory + "/h5/img"; System.out.println(strParentDirectory); String fileName = CommonUtils.generateUUID() + ".jpg"; String fileUrl = strParentDirectory + "/" + fileName; String imgStr = base64.split(",")[1]; Base64Utils.GenerateImage(imgStr, fileUrl); System.out.println(fileUrl); System.out.println(request.getServerName() + "/h5/img/" + fileName); return request.getServerName() + "/h5/img/" + fileName; // JSONObject json =new JSONObject(); // json.put("result",fileUrl); // response.setCharacterEncoding("utf-8"); // response.setContentType("application/json;charset=utf-8"); // PrintWriter out = null; // out = response.getWriter(); // out.write(json.toString()); // return json.toJSONString(); } @RequestMapping(value = "testPost") @ResponseBody @Login(required = false) public String testPost(HttpServletRequest request, String base64) { return ""; }
package com.jd.lestore.payment.common.utils; import com.jd.lestore.payment.common.constant.WeChatConstant; import com.jd.lestore.payment.common.constant.WeChatConstant.SignType; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.StringWriter; import java.security.MessageDigest; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; public class WeChatUtils { /** * 生成新的請求序列號 * * @return */ public static Integer getNewRequestSerial() { String timestamp = String.valueOf( System.currentTimeMillis()/1000 ); return Integer.valueOf(timestamp).intValue(); } public static String generateSignature(final Map<String, String> data, String key) throws Exception { return generateSignature(data, key, SignType.MD5); } public static String generateHMACSHA256Signature(final Map<String, String> data, String key) throws Exception { return generateSignature(data, key, SignType.HMACSHA256); } /** * 生成簽名 * * @param data * @param key * @return * @throws Exception 規則 * 第一步,設全部發送或者接收到的數據爲集合M,將集合M內非空參數值的參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 * <p> * 特別注意如下重要規則: * <p> * ◆ 參數名ASCII碼從小到大排序(字典序); * ◆ 若是參數的值爲空不參與簽名; * ◆ 參數名區分大小寫; * ◆ 驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值做校驗。 * ◆ 微信接口可能增長字段,驗證簽名時必須支持增長的擴展字段 * <p> * 第二步,在stringA最後拼接上key獲得stringSignTemp字符串,並對stringSignTemp進行MD5運算,再將獲得的字符串全部字符轉換爲大寫,獲得sign值signValue。 */ public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception { Set<String> keySet = data.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (k.equals(WeChatConstant.FIELD_SIGN)) { continue; } if (data.get(k).trim().length() > 0) // 參數值爲空,則不參與簽名 sb.append(k).append("=").append(data.get(k).trim()).append("&"); } sb.append("key=").append(key); if (SignType.MD5.equals(signType)) { return MD5(sb.toString()).toUpperCase(); } else if (SignType.HMACSHA256.equals(signType)) { System.out.println("HMACSHA256 original text: " + sb.toString()); return HMACSHA256(sb.toString(), key); } else { throw new Exception(String.format("Invalid sign_type: %s", signType)); } } /** * 生成 MD5 * * @param data 待處理數據 * @return MD5結果 */ public static String MD5(String data) throws Exception { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] array = md.digest(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 生成 HMACSHA256 * * @param data 待處理數據 * @param key 密鑰 * @return 加密結果 * @throws Exception */ public static String HMACSHA256(String data, String key) throws Exception { Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); sha256_HMAC.init(secret_key); byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 獲取當前時間戳,單位秒 * * @return */ public static long getCurrentTimestamp() { return System.currentTimeMillis() / 1000; } /** * 獲取當前時間戳,單位毫秒 * * @return */ public static long getCurrentTimestampMs() { return System.currentTimeMillis(); } /** * XML格式字符串轉換爲Map * * @param strXML XML字符串 * @return XML數據轉換後的Map * @throws Exception */ public static Map<String, String> xmlToMap(String strXML) throws Exception { InputStream stream = null; try { Map<String, String> data = new HashMap<String, String>(); DocumentBuilderFactory documentBuilderFactory = setWechatPaySafeCode(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } return data; } finally { try { stream.close(); } catch (Exception ex) { ex.printStackTrace(); } } } private static DocumentBuilderFactory setWechatPaySafeCode( ) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); // 禁 用 DOCTYPE try{ dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); //該 feature 的做用是配置是否包含外部的參數,包括外部 DTD 子集,設置false 禁用參數實體 dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); //該 feature 的功能指是否包含外部生成的實體,設置 false 禁用外部實體 dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); }catch ( Exception e ){ e.printStackTrace(); } return dbf; } /** * XML格式字符串轉換爲Map * * @param strXML XML字符串 * @return XML數據轉換後的Map * @throws Exception */ public static Map<String, String> xmlClildToMap(String strXML) throws Exception { InputStream stream = null; try { Map<String, String> data = new HashMap<String, String>(); DocumentBuilderFactory documentBuilderFactory = setWechatPaySafeCode(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; if(element.hasChildNodes()){//對於三級子項處理 for(int i = 0 ; i < element.getChildNodes().getLength();i++){ Node nodeChild = element.getChildNodes().item(i); data.put(nodeChild.getNodeName(),nodeChild.getTextContent()); } }else { data.put(element.getNodeName(), element.getTextContent()); } } } return data; } finally { try { stream.close(); } catch (Exception ex) { ex.printStackTrace(); } } } /** * 將Map轉換爲XML格式的字符串 * * @param data Map類型數據 * @return XML格式的字符串 * @throws Exception */ public static String mapToXml(Map<String, String> data) throws Exception { DocumentBuilderFactory documentBuilderFactory = setWechatPaySafeCode(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key : data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { ex.printStackTrace(); } return output; } /** * 將Map轉換爲XML格式的字符串 * * @param data Map類型數據 * @return XML格式的字符串 * @throws Exception */ public static String mapToXmlROOT(Map<String, String> data,String xmlRoot) throws Exception { DocumentBuilderFactory documentBuilderFactory = setWechatPaySafeCode(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); document.setXmlStandalone(true); org.w3c.dom.Element root = document.createElement(xmlRoot); document.appendChild(root); for (String key : data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { ex.printStackTrace(); } return output; } }