后台生成并返回前端所需参数
1:jsapi_ticket
jsapi_ticket是公众号用于调用js接口的临时票据。有效期7200秒,跟公众号普通access_token一样,必须全局缓存起来,因为这个ticket获取次数有限,超过次数将无法使用。
1.1获取access_token
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
1.2获取jsapi_ticket
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
2:生成签名
签名规则:
1、参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分)。
2、对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序,sort()即可)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串(string)。
3、使用sha1加密拼接成的字符串string。
注意:字段名和字段值都要使用原值,不要进行url转义
参与的字段:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=调用js接口的安全域名
2.1:校验签名
https://work.weixin.qq.com/api/jsapisign
3:后端完整代码
public net.sf.json.JSONObject getConfig(String url){
//url为前端动态入参
net.sf.json.JSONObject object = new net.sf.json.JSONObject();
String access_token = redisStringService.get("AccessToken");
String jsapi_ticket = redisStringService.get("JsapiTicket");
if(StringUtils.isEmpty(access_token)){
//获取access_token
String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+ wxConfigYml.getWxAppId()
+ "&secret="
+ wxConfigYml.getWxSecret();
String result = HttpUtil.sendGet(tokenUrl);
JSONObject accessObj = JSON.parseObject(result);
String accessToken = accessObj.getString("access_token");
redisStringService.put("AccessToken", accessToken, 7180);
}
if (StringUtils.isEmpty(jsapi_ticket)) {
//获取jsapi
String jsapiStr = HttpUtil.sendGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+access_token+"&type=jsapi");
net.sf.json.JSONObject jsapi = net.sf.json.JSONObject.fromObject(JSON.parseObject(jsapiStr));
jsapi_ticket = jsapi.getString("ticket");
redisStringService.put("JsapiTicket",jsapi_ticket,7180);
}
//获取签名signature
String noncestr = IdGen.uuid();
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
String str = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + noncestr +
"×tamp=" + timestamp +
"&url=" + url;
//sha1加密
String signature = WechatConfig.sha1(str);
object.put("noncestr",noncestr);
object.put("timestamp",timestamp);
object.put("signature",signature);
object.put("jsapi_ticket",jsapi_ticket);
log.info("jsapi_ticket="+jsapi_ticket);
log.info("noncestr="+noncestr);
log.info("timestamp="+timestamp);
log.info("url="+url);
log.info("signature="+signature);
return object;
}
3.1加密方法
public static String sha1(String str) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
3.2httpUtil
public class HttpUtil {
private static CloseableHttpClient httpclient = HttpClients.createDefault();
/**
* 发送HttpGet请求
* @param url
* @return
*/
public static String sendGet(String url) {
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpget);
} catch (IOException e1) {
e1.printStackTrace();
}
String result = null;
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 发送HttpPost请求,参数为map
* @param url
* @param map
* @return
*/
public static String sendPost(String url, Map map) {
List formparams = new ArrayList();
for (Map.Entry entry : map.entrySet()) {
formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
HttpPost httppost = new HttpPost(url);
httppost.setEntity(entity);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httppost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity1 = response.getEntity();
String result = null;
try {
result = EntityUtils.toString(entity1);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 发送不带参数的HttpPost请求
* @param url
* @return
*/
public static String sendPost(String url) {
HttpPost httppost = new HttpPost(url);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httppost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity = response.getEntity();
String result = null;
try {
result = EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String post(String url, String jsonString) {
CloseableHttpResponse response = null;
BufferedReader in = null;
String result = "";
try {
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setHeader("Accept", "application/json");
httpPost.setEntity(new StringEntity(jsonString, Charset.forName("UTF-8")));
response = httpclient.execute(httpPost);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
result = sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != response) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
4.常见问题
1、前端wx.config配置中的nonceStr字段名称的's'是大写。但是后台生成签名的noncestr字段 的‘s'是小写,千万要注意
2、时间戳(timestamp)值要记住精确到秒,不是毫秒。
3、生成签名的url(使用jssdk的页面地址,这个页面地址可以在浏览器访问),包含“?”号后面的所有参数,不包含“#”号后面的值。
如果是静默授权或者授权页面同意授权后跳转到的页面,页面路径会添加两个参数:code和state。
即授权后跳转页面为http://redirect.page.com,则完整路径为
http://redirect.page.com?code=kdijafdhjaikeiu20kaiela&state=STATE。
那么生成签名的url必须为授权后跳转页面的完整路径。前端获取这个路径:location.href.split('#')[0]