本部分須要用到微信的JS-SDK,微信JS-SDK是微信公衆平臺面向網頁開發者提供的基於微信內的網頁開發工具包。
經過使用微信JS-SDK,網頁開發者可藉助微信高效地使用拍照、選圖、語音、位置等手機系統的能力,同時能夠直接使用微信分享、掃一掃、卡券、支付等微信特有的能力,爲微信用戶提供更優質的網頁體驗。javascript
官方文檔
css
1.先登陸微信公衆平臺進入「公衆號設置」的「功能設置」裏填寫「JS接口安全域名」,和網頁受權同樣只是個域名。html
2.在須要調用JS接口的頁面引入以下JS文件之一java
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
wx.config({
debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。
appId: '', // 必填,公衆號的惟一標識
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名
jsApiList: [] // 必填,須要使用的JS接口列表
});
首先生成這個signature以前須要獲取到一個臨時票據jsapi_ticket,jsapi_ticket是公衆號用於調用微信JS接口的臨時票據。正常狀況下,jsapi_ticket的有效期爲7200秒,經過access_token來獲取。因爲獲取jsapi_ticket的api調用次數很是有限,頻繁刷新jsapi_ticket會致使api調用受限,影響自身業務,一樣也須要箇中控服務器控制刷新。jquery
package com.phil.wechatauth.model.resp; import com.phil.common.result.ResultState; /** * jsapi_ticket是公衆號用於調用微信JS接口的臨時票據 * @author phil * @date 2017年8月21日 * */ public class JsapiTicket extends ResultState { /** * */ private static final long serialVersionUID = -357009110782376503L; private String ticket; //jsapi_ticket private String expires_in; public String getTicket() { return ticket; } public void setTicket(String ticket) { this.ticket = ticket; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expires_in) { this.expires_in = expires_in; } }
獲取方法git
/** * 獲取jsapi_ticket 調用微信JS接口的臨時票據 * @return */ public String getTicket(String accessToken) { JsapiTicket jsapiTicket = null; Map<String,String> params = new TreeMap<String,String>(); params.put("access_token",accessToken); params.put("type", "jsapi"); String result = HttpReqUtil.HttpDefaultExecute(HttpReqUtil.GET_METHOD, WechatConfig.GET_TICKET_URL, params,""); if(StringUtils.isNotBlank(result)){ jsapiTicket = JsonUtil.fromJson(result, JsapiTicket.class); } if(jsapiTicket.getErrcode()==0){ return jsapiTicket.getTicket(); } return null; }
signature生成規則以下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其後面部分) 。對全部待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)後,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1。這裏須要注意的是全部參數名均爲小寫字符。對string1做sha1加密,字段名和字段值都採用原始值,不進行URL 轉義。github
string1示例以下web
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
這裏有個坑,頁面是nonceStr,可是簽名的字段是noncestr,注意大小寫spring
簡單封裝下JS-SDK config配置信息bootstrap
package com.phil.wechatauth.model.resp; /** * JS-SDK的頁面配置信息 * @author phil * @date 2017年8月22日 * */ public class JsWechatConfig { private String appId; private long timestamp; private String noncestr; private String signature; public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public String getNoncestr() { return noncestr; } public void setNoncestr(String noncestr) { this.noncestr = noncestr; } public String getSignature() { return signature; } public void setSignature(String signature) { this.signature = signature; } }
添加配置信息到頁面
/** * */ package com.phil.wechatauth.controller; import java.util.SortedMap; import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.phil.common.config.SystemConfig; import com.phil.common.config.WechatConfig; import com.phil.common.util.DateTimeUtil; import com.phil.common.util.PayUtil; import com.phil.common.util.SignatureUtil; import com.phil.wechatauth.model.resp.JsWechatConfig; import com.phil.wechatauth.service.WechatAuthService; /** * JS-SDK * @author phil * @date 2017年8月21日 * */ @Controller @RequestMapping("/auth") public class WechatAuthController { @Autowired private WechatAuthService wechatAuthService; /** * 獲取地理位置 * @param request * @return * @throws Exception */ @RequestMapping("/getLocation") public String getLocation(HttpServletRequest request) throws Exception{ JsWechatConfig jsWechatConfig = new JsWechatConfig(); jsWechatConfig.setAppId(WechatConfig.APP_ID); jsWechatConfig.setTimestamp(DateTimeUtil.currentTime()); jsWechatConfig.setNoncestr(PayUtil.createNonceStr()); SortedMap<Object,Object> map = new TreeMap<Object,Object>(); map.put("jsapi_ticket", wechatAuthService.getTicket(wechatAuthService.findlastestToken())); map.put("noncestr", jsWechatConfig.getNoncestr()); map.put("timestamp", jsWechatConfig.getTimestamp()); map.put("url", request.getRequestURL().toString()); String signature = SignatureUtil.createSha1Sign(map, null, SystemConfig.CHARACTER_ENCODING); jsWechatConfig.setSignature(signature); request.setAttribute("jsWechatConfig", jsWechatConfig); return "wechatauth/getLocation"; } }
簽名方法
/** * 經過Map<SortedMap,Object>中的全部元素參與簽名 * * @param map 待參與簽名的map集合 * @params apikey apikey中 若是爲空則不參與簽名,若是不爲空則參與簽名 * @return */ public static String createSha1Sign(SortedMap<Object, Object> map, String apiKey, String characterEncoding) { String result = notSignParams(map, apiKey); MessageDigest md = null; try { md = MessageDigest.getInstance("SHA-1"); byte[] digest = md.digest(result.getBytes()); result = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return result; }
其餘的簽名方法點擊查看
以上執行完成,進入的完整的頁面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>獲取地理位置</title> <!-- 微信 js-sdk --> <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"> <script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script> </head> <body> <br> <div class="container"> <div class="form-group"> <label for="firstname" class="col-sm-2 control-label">地址:</label> <div class="col-sm-10" id="item-ifo"> <input type="text" value="" class="form-control" name="location.address" id="address" placeholder="正在獲取地理位置" tabindex="1" autocomplete="off" /> <div class="i-name ico" id="i-name"></div> </div> </div> </div> </body> <script type="text/javascript"> wx.config({ debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。 appId: '${jsWechatConfig.appId}', // 必填,公衆號的惟一標識 timestamp: '${jsWechatConfig.timestamp}' , // 必填,生成簽名的時間戳 nonceStr: '${jsWechatConfig.noncestr}', // 必填,生成簽名的隨機串 signature: '${jsWechatConfig.signature}',// 必填,簽名,見附錄1 jsApiList: [ 'checkJsApi', 'openLocation', 'getLocation'] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2 }); wx.checkJsApi({ jsApiList: ['getLocation'], // 須要檢測的JS接口列表,全部JS接口列表見附錄2, success: function(res) { if (res.checkResult.getLocation == false) { alert('你的微信版本過低,不支持微信JS接口,請升級到最新的微信版本!'); return; } } }); var latitude; var longitude; var speed; var accuracy; wx.ready(function(){ // config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。 wx.getLocation({ success : function(res) { latitude = res.latitude; // 緯度,浮點數,範圍爲90 ~ -90 longitude = res.longitude; // 經度,浮點數,範圍爲180 ~ -180。 speed = res.speed; // 速度,以米/每秒計 accuracy = res.accuracy; // 位置精度 alert(latitude); alert(accuracy); }, cancel : function(res) { alert('未能獲取地理位置'); } }); }); wx.error(function(res){ // config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。 alert("驗證出錯"); }); </script> </html>
能夠經過微信官方提供的微信web開發者工具調試,若是提示無效的簽名的話檢查下簽名字段是不是小寫
工具類我已經上傳到Github,點擊參考