获取access_token需要两个东西,appID和appsecret,测试账号在此申请即用
测试账号的access_token每天做多获取2000次,最好是缓存起来,避免获取次数过多达到上限,申请测试账号之后,拿到appID和appsecret访问下列地址即可获取
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
获取access_token代码:
public synchronized String getAccessToken() {
Object redisToken = redisTemplate.opsForValue().get("accessToken");
if (redisToken == null) {
String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + "client_credential"
+ "&appid=" + appId + "&secret=" + appSecret;
// 获取accessToken
String accessTokenBody = cn.hutool.http.HttpUtil.get(accessTokenUrl);
log.info("获取accessToken{}", accessTokenBody);
com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(accessTokenBody);
String accessToken = null;
try {
accessToken = (String) jsonObject.get("access_token");
log.info("accessToken:{}", accessToken);
} catch (Exception e) {
log.error("获取accessToken异常");
}
// 存储一个小时
redisTemplate.opsForValue().set("accessToken", accessToken, Constant.GenericNumber.NUMBER_ONE, TimeUnit.HOURS);
return accessToken;
} else {
return redisToken.toString();
}
}
使用第一步获取的access_token访问下述地址获取
采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
这一步也是需要进行缓存的,具体代码如下
private synchronized String getTicket(String accessToken) {
Object jsapiTicket = redisTemplate.opsForValue().get("jsapiTicket");
if (jsapiTicket == null) {
String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken
+ "&type=jsapi";
// 获取getTicketResult
String getTicketResult = cn.hutool.http.HttpUtil.get(getTicketUrl);
JSONObject ticketResultObject = JSONObject.parseObject(getTicketResult);
String ticket = null;
try {
ticket = (String) ticketResultObject.get("ticket");
log.info("ticket:{}", ticket);
} catch (Exception e) {
log.error("获取ticket异常");
}
// 缓存中的accessToken可能不是最新的,导致获取ticket失败
// 清除redis缓存的accessToken重新获取 不使用递归避免其他原因获取失败而重复调用
if (ticket == null) {
redisTemplate.delete("accessToken");
return getTicket(accessToken);
}
return ticket;
} else {
return jsapiTicket.toString();
}
}
签名生成规则如下:
参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳),url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用 URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL
转义。
即signature=sha1(string1)。 示例:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value
注意!注意!注意!
JAVA获取的时间戳是到毫秒级的,但是这里的时间戳是到秒的,需要换算之后才可以,生成之后的键值对一共是四组,不能有半组存在,尤其是开头的 “jsapi_ticket= ” 一定要有
SHA-1加密工具类:
private String encryptSHA(String signStr) {
StringBuffer hexValue = new StringBuffer();
MessageDigest sha = null;
try {
sha = MessageDigest.getInstance("SHA-1");
byte[] byteArray = signStr.getBytes("UTF-8");
byte[] md5Bytes = sha.digest(byteArray);
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
} catch (Exception e) {
e.printStackTrace();
return "";
}
return hexValue.toString();
}
获取签名代码:
public cn.hutool.json.JSONObject createJsapiTicket(String url) {
String accessToken = getAccessToken();
String ticket = getTicket(accessToken);
long timestamp = System.currentTimeMillis() / 1000;
String noncestr = "letsschool";
String signStr = new StringBuilder("jsapi_ticket=")
.append(ticket)
.append("&noncestr=")
.append(noncestr)
.append("×tamp=")
.append(timestamp)
.append("&url=")
.append(url)
.toString();
log.info("signStr:{}", signStr);
String signature = encryptSHA(signStr);
cn.hutool.json.JSONObject returnJson = JSONUtil.createObj();
returnJson.put("timestamp", timestamp);
returnJson.put("ticket", ticket);
returnJson.put("noncestr", noncestr);
returnJson.put("signature", signature);
returnJson.put("appId", appId);
return returnJson;
}