本篇主要叙述Java对接企微必要信息等,后续接口根据各接口需求来获取企微必要信息(access_token、jsapi_ticket等),此处附上企微开发接口文档
获取企微access_token
获取access_token是调用企业微信API接口的第一步,相当于创建了一个登录凭证,其它的业务API接口,都需要依赖于access_token来鉴权调用者身份。
请求参数:
参数 | 必须 | 说明 |
corpid | 是 | 企业ID |
corpsecret | 是 | 应用的凭证密钥,注意应用需要是启用状态 |
企业ID:每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业ID”(需要有管理员权限)
凭证秘钥:在管理后台->“应用管理”->“应用”->“自建”,点进某个应用,即可看到。
返回结果:
参数 | 说明 |
errcode | 出错返回码,为0表示成功,非0表示调用失败 |
errmsg | 返回码提示语 |
access_token | 获取到的凭证,最长为512字节 |
expires_in | 凭证的有效时间(秒) |
注意事项:对于access_token的获取官方规定不能频繁调用gettoken接口,否则会受到频率拦截,所以一般选择缓存,此文选择用Redis进行缓存。access_token的有效期正常情况下为7200秒(2小时)。
Java中获取access_token的代码实例如下
public String getAccessToken() {
//从缓存中获取access_token
String weComAccessToken = (String) redisTemplate.opsForValue().get(WECOM_ACCESS_TOKEN);
if (!StringUtils.isEmpty(weComAccessToken)) {
return weComAccessToken;
}
try {
//这里的企业id和企业秘钥是在配置文件中获取,此处修改看个人。
String corpid = weComProperties.getClientId();
String corpsecret = weComProperties.getClientSecret();
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + clientId + "&corpsecret=" + clientSecret;
String resultStr = HttpUtil.get(url);
JSONObject result = JSONObject.parseObject(resultStr);
//非空判断
if (result == null) {
throw exception(DATA_NOT_EXIST);
}
String accessToken = result.getString("access_token");
String expiresIn = result.getString("expires_in");
//存入redis
redisTemplate.opsForValue().set(WECOM_ACCESS_TOKEN, accessToken,Long.valueOf(expiresIn), TimeUnit.SECONDS);
return result.getString("access_token");
} catch (ServiceException e) {
throw new RuntimeException(e);
}
}
获取企业jsapi_ticket
jsapi_ticket是H5应用调用企业微信JS接口的临时票据,获取企微的jsapi_ticket与获取access_token过程大致相同。
请求参数:
参数 | 必须 | 说明 |
access_token | 是 | 调用接口凭证 |
返回参数:
参数 | 说明 |
errcode | 出错返回码,为0表示成功,非0表示调用失败 |
errmsg | 返回码提示语 |
ticket | 生成签名所需的jsapi_ticket,最长为512字节 |
expires_in | 凭证的有效时间(秒) |
注意事项:此处获取次数官方也有做次数限制(一小时内,一个企业最多可获取400次,且单个应用不能超过100次,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务),故需要做缓存处理,此处做的也是缓存也是存入Redis中。正常情况下,jsapi_ticket的有效期为7200秒。
Java中获取jsapi_ticket的代码实例如下
public String getJsapiTicket() {
//缓存中获取数据
String jsapiTicket = (String) redisTemplate.opsForValue().get(WECOM_JSAPI_TICKET);
if (!StringUtils.isEmpty(jsapiTicket)) {
return jsapiTicket;
}
try {
String accessToken = getAccessToken();
String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=" + accessToken+"&type=agent_config";
String resultStr = HttpUtil.get(url);
JSONObject result = JSONObject.parseObject(resultStr);
if (result == null) {
throw exception(DATA_NOT_EXIST);
}
String ticket = result.getString("ticket");
String expiresIn = result.getString("expires_in");
//存入redis
redisTemplate.opsForValue().set(WECOM_JSAPI_TICKET, ticket,Long.valueOf(expiresIn), TimeUnit.SECONDS);
return result.getString("ticket");
} catch (ServiceException e) {
throw new RuntimeException(e);
}
}
JS-SDK相关参数
前端在处理企微JS-SDK需要通过config接口注入权限验证配置,那配置中的一些参数则后端做处理后传给前端,所以此处做的是将相关参数传给前端。
wx.config({
beta: true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,企业微信的corpID,必须是本企业的corpID,不允许跨企业使用
timestamp: '', // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见 附录-JS-SDK使用权限签名算法
jsApiList: [] // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
});
这里传的参数需要跟前端对接好,本文处理的参数为appId、timestamp、nonceStr、signature。
故创建WeComRespVO对象:
@Data
@Builder
public class WeComRespVO {
/**
* 时间戳
*/
private String timestamp;
/**
* 随机字符串
*/
private String noncestr;
/**
* 企业微信的corpID
*/
private String appId;
/**
* 签名
*/
private String signature;
}
主要的签名规则如下:
参与签名的参数有四个: noncestr(随机字符串), jsapi_ticket(如何获取参考“获取企业jsapi_ticket”、“获取应用的jsapi_ticket接口”以及“获取第三方应用的jsapi_ticket接口”), timestamp(时间戳), url(当前网页的URL, 不包含#及其后面部分)
注意事项:
- 字段值采用原始值,不要进行URL转义;
- 必须严格按照如下格式拼接,不可变动字段顺序。
拼接顺序:jsapi_ticket=JSAPITICKET&noncestr=NONCESTR×tamp=TIMESTAMP&url=URL
拼接完后对拼接完字符串做sha1加密处理即可。
代码实例:
public WeComRespVO getSignature(String url) {
String jsapiTicket = getJsapiTicket();
//生成随机数(此处做简便处理,各位按各自方式生成)
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
//时间戳
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
//拼接,url是需要前端传进来的。
String string1 = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;
//对string1进行sha1签名,得到signature
String signature = sha1Sign(string1);
WeComRespVO weComRespVO = WeComRespVO.builder().signature(signature).noncestr(noncestr).timestamp(timestamp).appId(weComProperties.getClientId()).build();
return weComRespVO;
}
private String sha1Sign(String input) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(input.getBytes());
byte[] digest = md.digest();
StringBuilder hexString = new StringBuilder();
for (byte b : digest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to find SHA1 algorithm", e);
}
}
整体代码如下:
@Data
@Builder
public class WeComRespVO {
/**
* 时间戳
*/
private String timestamp;
/**
* 随机字符串
*/
private String noncestr;
/**
* 企业微信的corpID
*/
private String appId;
/**
* 签名
*/
private String signature;
}
@Service
public class WeComClientServiceImpl implements WeComClientService {
@Resource
private WeComProperties weComProperties;
@Resource
private RedisTemplate redisTemplate;
public String getAccessToken() {
String weComAccessToken = (String) redisTemplate.opsForValue().get(WECOM_ACCESS_TOKEN);
if (!StringUtils.isEmpty(weComAccessToken)) {
return weComAccessToken;
}
try {
String clientId = weComProperties.getClientId();
String clientSecret = weComProperties.getClientSecret();
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + clientId + "&corpsecret=" + clientSecret;
String resultStr = HttpUtil.get(url);
JSONObject result = JSONObject.parseObject(resultStr);
if (result == null) {
throw exception(DATA_NOT_EXIST);
}
String accessToken = result.getString("access_token");
String expiresIn = result.getString("expires_in");
//存入redis
redisTemplate.opsForValue().set(WECOM_ACCESS_TOKEN, accessToken,Long.valueOf(expiresIn), TimeUnit.SECONDS);
return result.getString("access_token");
} catch (ServiceException e) {
throw new RuntimeException(e);
}
}
public String getJsapiTicket() {
String jsapiTicket = (String) redisTemplate.opsForValue().get(WECOM_JSAPI_TICKET);
if (!StringUtils.isEmpty(jsapiTicket)) {
return jsapiTicket;
}
try {
String accessToken = getAccessToken();
String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=" + accessToken+"&type=agent_config";
String resultStr = HttpUtil.get(url);
JSONObject result = JSONObject.parseObject(resultStr);
if (result == null) {
throw exception(DATA_NOT_EXIST);
}
String ticket = result.getString("ticket");
String expiresIn = result.getString("expires_in");
//存入redis
redisTemplate.opsForValue().set(WECOM_JSAPI_TICKET, ticket,Long.valueOf(expiresIn), TimeUnit.SECONDS);
return result.getString("ticket");
} catch (ServiceException e) {
throw new RuntimeException(e);
}
}
public WeComRespVO getSignature(String url) {
String jsapiTicket = getJsapiTicket();
//生成随机数
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
//时间戳
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
//拼接
String string1 = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;
//对string1进行sha1签名,得到signature
String signature = sha1Sign(string1);
WeComRespVO weComRespVO = WeComRespVO.builder().signature(signature).noncestr(noncestr).timestamp(timestamp).appId(weComProperties.getClientId()).build();
return weComRespVO;
}
private String sha1Sign(String input) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(input.getBytes());
byte[] digest = md.digest();
StringBuilder hexString = new StringBuilder();
for (byte b : digest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to find SHA1 algorithm", e);
}
}
}