JS-SDK使用权限签名算法
2016-10-27 15:23
627 查看
jsapi_ticket
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒(2小时),通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token): http://mp.weixin.qq.com/wiki/15/54ce45d8d30b6bf6758f68d2e95bc627.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
Token也就是用户身份凭证的意思。access_token
是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在微信公众平台官网-开发者中心页中获得(需要已经成为开发者,且帐号没有异常状态)。注意调用所有微信接口时均需使用https协议。
接口调用请求说明:
http请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
1. 证书信任管理器
对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,代码如下:
这个证书管理器的作用就是让它信任我们指定的证书,上面的代码意味着信任所有证书,不管是否权威机构颁发。
2. 通用的 https 请求方法:
调用获取凭证接口后,微信服务器会返回json格式的数据:{"access_token":"ACCESS_TOKEN","expires_in":7200},我们将其封装为一个AccessToken对象,对象有二个属性:token和expiresIn,代码如下:
4. 获取 access_token 访问凭证
在前面的第2步中的通用Util 类 WeixinUtil 里 添加获取 token 的方法:
5. pojo 类 JsApiTicket 临时票据
6. 根据前面获取到的 access_token 访问凭证来获取 jsapi_ticket 临时票据
7. 全局缓存 access_token 和 jsapi_ticket
参考实现:
(1).存到 servletcontext 中
http://blog.csdn.net/cnm_1314/article/details/51383411
(2).缓存到 map 中
http://www.cnblogs.com/setukirin/p/5718059.html
(3).缓存到 xml 中
http://www.cnblogs.com/superstar/p/5159936.html
除此之外,还可以放到数据库中,只不过放到数据库中就是每次还的建立数据库连接再读取,略有麻烦;也还可以放到 Memcache 中(SAE为开发者提供的分布式缓存服务,用来以共享的方式缓存用户的少量数据,用户可以使用标准的memcache函数读写Memcache),但 memcache需要先安装才能使用。所有个人认为最简单的就是缓存在静态变量map中。
尊重他人劳动成果,以前有部分内容参考自:http://blog.csdn.net/gebitan505/article/details/24559181
以下来自微信JSSDK官方说明文档demo:http://demo.open.weixin.qq.com/jssdk/sample.zip
Java 签名算法 demo:
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒(2小时),通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token): http://mp.weixin.qq.com/wiki/15/54ce45d8d30b6bf6758f68d2e95bc627.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
Token也就是用户身份凭证的意思。access_token
是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在微信公众平台官网-开发者中心页中获得(需要已经成为开发者,且帐号没有异常状态)。注意调用所有微信接口时均需使用https协议。
接口调用请求说明:
http请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
1. 证书信任管理器
对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,代码如下:
package org.common.weixin.util; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * 证书信任管理器(用于https请求) */ public class MyX509TrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }
这个证书管理器的作用就是让它信任我们指定的证书,上面的代码意味着信任所有证书,不管是否权威机构颁发。
2. 通用的 https 请求方法:
package org.common.weixin.util; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import net.sf.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 公众平台通用接口工具类 * */ public class WeixinUtil { private static Logger log = LoggerFactory.getLogger(WeixinUtil.class); /** * 发起https请求并获取结果 * * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; StringBuffer buffer = new StringBuffer(); 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 httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 设置请求方式(GET/POST) 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.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("Weixin server connection timed out."); } catch (Exception e) { log.error("https request error:{}", e); } return jsonObject; } }3. Pojo类 - AccessToken
调用获取凭证接口后,微信服务器会返回json格式的数据:{"access_token":"ACCESS_TOKEN","expires_in":7200},我们将其封装为一个AccessToken对象,对象有二个属性:token和expiresIn,代码如下:
public class AccessToken { // 获取到的凭证 private String token; // 凭证有效时间,单位:秒 private int expiresIn; public String getToken() { return token; } public void setToken(String token) { this.token = token; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } } </span><span style="font-size: 14px;"> </span>
4. 获取 access_token 访问凭证
在前面的第2步中的通用Util 类 WeixinUtil 里 添加获取 token 的方法:
// 获取access_token的接口地址(GET) 限200(次/天) public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; /** * 获取access_token * * @param appid 凭证 * @param appsecret 密钥 * @return */ public static AccessToken getAccessToken(String appid, String appsecret) { AccessToken accessToken = null; String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); //调用通用的https请求方法 // 如果请求成功 if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setToken(jsonObject.getString("access_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in")); } catch (JSONException e) { accessToken = null; // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return accessToken; }
5. pojo 类 JsApiTicket 临时票据
public class JsApiTicket { private String ticket; //票据 private int expiresIn; <span style="font-family: Arial, Helvetica, sans-serif;">// 凭证有效时间,单位:秒</span> public String getTicket() { return ticket; } public void setTicket(String ticket) { this.ticket = ticket; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } }
6. 根据前面获取到的 access_token 访问凭证来获取 jsapi_ticket 临时票据
public final static String ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi"; public static JsApiTicket getJsApiTicket(String accessToken) { JsApiTicket jsApiTicket = null; //获取token String acess_token= WeixinUtil.getAccessToken(); String requestUrl = ticket_url.replace("ACCESS_TOKEN", acess_token); JSONObject jsonObject = WeixinUtil.httpRequest(requestUrl, "GET", null); // 如果请求成功 if (null != jsonObject) { try { jsApiTicket = new JsApiTicket(); jsApiTicket.setTicket(jsonObject.getString("ticket")); jsApiTicket.setExpiresIn(jsonObject.getInt("expires_in")); } catch (JSONException e) { accessToken = null; // 获取jsApiTicket失败 log.error("获取jsApiTicket失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return jsApiTicket; }
7. 全局缓存 access_token 和 jsapi_ticket
参考实现:
(1).存到 servletcontext 中
http://blog.csdn.net/cnm_1314/article/details/51383411
(2).缓存到 map 中
http://www.cnblogs.com/setukirin/p/5718059.html
(3).缓存到 xml 中
http://www.cnblogs.com/superstar/p/5159936.html
除此之外,还可以放到数据库中,只不过放到数据库中就是每次还的建立数据库连接再读取,略有麻烦;也还可以放到 Memcache 中(SAE为开发者提供的分布式缓存服务,用来以共享的方式缓存用户的少量数据,用户可以使用标准的memcache函数读写Memcache),但 memcache需要先安装才能使用。所有个人认为最简单的就是缓存在静态变量map中。
//创建一个静态map缓存 private static Map<String,Ticket> weixinCache = new HashMap<String, Ticket>(); private static JsApiTicket getJsApiTicket(String accessToken){ String ticket_str = null; JsApiTicket jsApiTicket = weixinCache.get("ticket"); if(jsApiTicket != null){ Date expiresIn = jsApiTicket.getExpiresIn(); Calendar ever = Calendar.getInstance(); ever.setTime(expiresIn); Calendar current = Calendar.getInstance(); if(ever.after(current)){ ticket_str = jsApiTicket.getTicket(); }else{ String request_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi".replace("ACCESS_TOKEN", accessToken); JSONObject jsonObject = WeixinUtils.httpRequest(request_url, "GET", null); ticket_str = jsonObect.getString("ticket"); weixinCache.remove("ticket"); JsApiTicket ticket2 = new Ticket(); ticket2.setExpirseIn(jsonObect.getString("expirseIn")); ticket2.setTicket(ticket_str); weixinCache.put("ticket", ticket2); } }else{ //同上面的else里的code } return jsApiTicket; }
尊重他人劳动成果,以前有部分内容参考自:http://blog.csdn.net/gebitan505/article/details/24559181
以下来自微信JSSDK官方说明文档demo:http://demo.open.weixin.qq.com/jssdk/sample.zip
Java 签名算法 demo:
import java.util.UUID; import java.util.Map; import java.util.HashMap; import java.util.Formatter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.io.UnsupportedEncodingException; class Sign { public static void main(String[] args) { String jsapi_ticket = "jsapi_ticket"; // 注意 URL 一定要动态获取,不能 hardcode String url = "http://example.com"; Map<String, String> ret = sign(jsapi_ticket, url); for (Map.Entry entry : ret.entrySet()) { System.out.println(entry.getKey() + ", " + entry.getValue()); } }; public static Map<String, String> sign(String jsapi_ticket, String url) { Map<String, String> ret = new HashMap<String, String>(); String nonce_str = create_nonce_str(); String timestamp = create_timestamp(); String string1; String signature = ""; //注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; System.out.println(string1); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); //对string1 字符串进行SHA-1加密处理 signature = byteToHex(crypt.digest()); //对加密后字符串转成16进制 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put("url", url); ret.put("jsapi_ticket", jsapi_ticket); ret.put("nonceStr", nonce_str); ret.put("timestamp", timestamp); ret.put("signature", signature); return ret; } 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; } //生成随机字符串 private static String create_nonce_str() { return UUID.randomUUID().toString(); } //生成时间戳字符串 private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } }
相关文章推荐
- 微信JS SDK使用权限签名算法
- 微信JS SDK使用权限签名算法 jsapi_ticket
- 微信JS-SDK使用权限签名算法的服务端实现(.net版本)
- 微信JS SDK使用权限签名算法
- JS-SDK使用权限签名算法----调用c++后台服务进行签名认证
- 微信JS SDK使用权限签名算法
- 微信JS SDK使用权限签名算法 jsapi_ticket
- 微信JS-SDK使用权限签名算法example
- 微信JS-SDK使用权限签名算法的服务端实现(.net版本)
- JS-SDK使用权限签名算法
- PHP+TP框架实现获取微信JS-SDK使用权限签名算法需要的jsapi_ticket,并全局缓存
- 微信JS-SDK使用权限签名算法的服务端实现(.net版本)
- 微信JS SDK使用权限签名算法
- 微信JS-SDK使用权限签名算法的服务端实现(.net版本)
- 微信JS-SDK 权限签名算法 C#版
- 钉钉客户端JS-API权限签名算法.NET版
- C#微信网页开发---JSSDK使用 通过config接口注入权限验证配置
- 微信公众号用java中控服务器-实现用户网页授权和获取使用JS-SDK权限
- 微信企业号开发之获取jsapi_ticket并生成JS-SDK权限验证签名
- 使用堆实现Top K 算法 JS 实现