使用说明:
在使用微信JS-SDK之前必须确认公众号已经获得使用微信js接口的权限,并且已填写安全域名,可登陆微信公众号平台查看。
1、引入微信js
2、根据需求添加js功能
生成js页面需要的参数 及签名的生成:
1、获取access_token, 因为一般有效期是7200s,所以添加到了ehcache中,默认先从缓存中获取,失效后再发送GET请求获取
Object act = EhcacheUtil.getInstance().get("weixin_jsapi", "access_token"); Object apiticket = EhcacheUtil.getInstance().get("weixin_jsapi", "ticket"); logger.debug("[act] = " + act + " [apiticket] = " + apiticket); if (null == act) { String url = "https://api.weixin.qq.com/cgi-bin/token"; String jsonStrToken = Tools.sendGet(url, "grant_type=client_credential&appid="+ appId + "&secret=" + appSecret); logger.debug("[jsonStrToken] = " + jsonStrToken); JSONObject json = JSONObject.fromObject(jsonStrToken); access_token = (String) json.getString("access_token"); if (access_token == null) { return null; } EhcacheUtil.getInstance().put("weixin_jsapi", "access_token", access_token); } else { access_token = (String) act; }
2、根据上一步中的access_token获取jsapi_ticket:
if (null == apiticket) { String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"; String jsonStrTicket = Tools.sendGet(url, "access_token=" + access_token + "&type=jsapi"); logger.debug("[jsonStrTicket] = " + jsonStrTicket); JSONObject json = JSONObject.fromObject(jsonStrTicket); ticket = (String) json.get("ticket"); } else { ticket = (String) apiticket; }
3、获取当前需要分享的网页的URL:
//获取URL String url = request.getRequestURL().toString();
4、最终生成签名:
(1)获取时间戳:
private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); }
(2)获取随机字符串:
private static String create_nonce_str() { return UUID.randomUUID().toString(); }
(3)将jsapi_ticket、noncestr、timestamp、url拼接,使用SHA1加密算法,最终生成签名
// 注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; logger.debug("[string1] = " + string1); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); logger.debug("[signature] = " + signature); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); }
5、将服务端生成的noncestr、timestamp、signature设置到model(使用的是spring mvc)中,返回到页面:
Mapret = sign.sign(jsapi_ticket, url); mav.addObject("ret", ret); var timestamp = parseInt('${ret.timestamp}'); var nonceStr = '${ret.nonceStr}'; var signature = '${ret.signature}'; wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: 'wxa034b7003154ee6c', // 必填,公众号的唯一标识 timestamp: timestamp, // 必填,生成签名的时间戳 nonceStr: nonceStr, // 必填,生成签名的随机串 signature: signature,// 必填,签名,见附录1 jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 });
完整的服务端代码:
1、controller层:
//获取ticket String jsapi_ticket = weiXinRequest.getWeiXinTicket(); logger.debug("[jsapi_ticket] = " + jsapi_ticket); //获取URL String url = request.getRequestURL().toString(); Mapret = sign.sign(jsapi_ticket, url); mav.addObject("ret", ret); logger.debug("[ret] = " + ret); return mav;
2、weiXinRequest
import net.sf.json.JSONObject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Component; @Component("WeiXinRequest") public class WeiXinRequest { private String appId = "wxa034b7003154ee6c"; private String appSecret = "7fce4f1ee0f63b62f20fe8321a31dea8"; private Log logger = LogFactory.getLog(WeiXinRequest.class); public String getWeiXinTicket() throws Exception { String access_token = ""; String ticket = ""; Object act = EhcacheUtil.getInstance().get("weixin_jsapi", "access_token"); Object apiticket = EhcacheUtil.getInstance().get("weixin_jsapi", "ticket"); logger.debug("[act] = " + act + " [apiticket] = " + apiticket); if (null == act) { String url = "https://api.weixin.qq.com/cgi-bin/token"; String jsonStrToken = Tools.sendGet(url, "grant_type=client_credential&appid="+ appId + "&secret=" + appSecret); logger.debug("[jsonStrToken] = " + jsonStrToken); JSONObject json = JSONObject.fromObject(jsonStrToken); access_token = (String) json.getString("access_token"); if (access_token == null) { return null; } EhcacheUtil.getInstance().put("weixin_jsapi", "access_token", access_token); } else { access_token = (String) act; } if (null == apiticket) { String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"; String jsonStrTicket = Tools.sendGet(url, "access_token=" + access_token + "&type=jsapi"); logger.debug("[jsonStrTicket] = " + jsonStrTicket); JSONObject json = JSONObject.fromObject(jsonStrTicket); ticket = (String) json.get("ticket"); } else { ticket = (String) apiticket; } return ticket; // 断开连接 } }
3、Sign签名类:
import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component("Sign") public class Sign { @Autowired private WeiXinRequest weiXinRequest; private Log logger = LogFactory.getLog(Sign.class); public Maptest(HttpServletRequest requesturl) throws Exception { String ticket = weiXinRequest.getWeiXinTicket(); // 注意 URL 一定要动态获取,不能 hardcode String url = requesturl.getRequestURL().toString(); Map ret = sign(ticket, url); for (Map.Entry entry : ret.entrySet()) { System.out.println(entry.getKey() + ", " + entry.getValue()); } // ret.put("appId", weiXinRequest.appId); return ret; }; public Map sign(String jsapi_ticket, String url) { Map ret = new HashMap (); 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; logger.debug("[string1] = " + string1); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); logger.debug("[signature] = " + signature); } 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); logger.debug("[ret] = " + ret); 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); } /** * 用SHA1算法生成安全签名 * * @param token * 票据 * @param timestamp * 时间戳 * @param nonce * 随机字符串 * @param encrypt * 密文 * @return 安全签名 * @throws NoSuchAlgorithmException * @throws AesException */ public String getSHA1(String token, String timestamp, String nonce) throws NoSuchAlgorithmException { String[] array = new String[] { token, timestamp, nonce }; StringBuffer sb = new StringBuffer(); // 字符串排序 Arrays.sort(array); for (int i = 0; i < 3; i++) { sb.append(array[i]); } String str = sb.toString(); // SHA1签名生成 MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(str.getBytes()); byte[] digest = md.digest(); StringBuffer hexstr = new StringBuffer(); String shaHex = ""; for (int i = 0; i < digest.length; i++) { shaHex = Integer.toHexString(digest[i] & 0xFF); if (shaHex.length() < 2) { hexstr.append(0); } hexstr.append(shaHex); } return hexstr.toString(); } }
4、发送请求类Tools 这里用的是 Url 、UrlConnection 类 类似于httpClient
/** * 向指定URL发送GET方法的请求 * * @param url * 发送请求的URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return URL 所代表远程资源的响应结果 */ public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 URLConnection connection = realUrl.openConnection(); // 设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); // 建立实际的连接 connection.connect(); // 获取所有响应头字段 Map> map = connection.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader( connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 发送POST方法的请求 * * @param url * 发送请求的 URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!"+e); e.printStackTrace(); } //使用finally块来关闭输出流、输入流 finally{ try{ if(out!=null){ out.close(); } if(in!=null){ in.close(); } } catch(IOException ex){ ex.printStackTrace(); } } return result; }