前言
本文主题是配置 wx.config 实现“屏蔽企业微信分享功能”,原本我以为这个企业微信屏蔽分享功能和微信应该差不多,等到我真正配置去写的时候才发现。。这玩意坑还挺多,写篇文章记录一下自己踩过的坑,希望对你们有所帮助!
概述
先简单说一下什么 JS-SDK 的作用,企业微信 JS-SDK 是企业微信面向网页开发者提供的基于企业微信内的网页开发工具包,具体详情查看企业微信官方文档:JS-SDK
关于微信和企业微信调用屏蔽功能之间的区别:微信内置浏览器有自带的私有接口可直接调用 WeixinJSBridge(关于这个接口的介绍可查看博主的上一篇文章“微信开发:屏蔽分享功能”)我原本以为这个接口是企业微信和微信可以共用的后来才发现,这个接口仅限于“微信内置浏览器”
测试工具及文档
1.签名效验工具
2.wx.config 配置文档
3.JS-SDK使用权限签名算法
3.JS-SDK Demo
配置 wx.config
第一步:引入 jweixin-1.2.0.js 文件,在那个页面用就引入到那个页面,(支持 https):点击下载,注意这个地方不支持微信的 jweixin-1.6.0.js 版本
<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
第二步:配置 wx.config,这个地方有个需要注意的点,为了安全起见不建议使用 ajax 回调的方式获取签名参数,推荐进入页面时获取。
wx.config({
beta: true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,企业微信的corpID
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见 附录-JS-SDK使用权限签名算法
jsApiList: [] // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
});
第三步:这个地方比较重要,所有调用的 wx.config 成功的接口一定要写到这个里边 wx.ready(),相当于是一个处理成功验证
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
第四步:这个就没啥好介绍的。。error 接口处理失败验证
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
具体参数的介绍差不多也就这些。。下边就是我们的实践操作
代码片段
这个地方我们先写后台获取签名等配置,这个地方的配置参数要和我们 wx.config 对应,一定不要错!!
1.获取 timestamp 参数(获取随机时间戳)
// 时间戳
String timestamp = Long.toString((new Date().getTime()) / 1000);
2.获取 nonceStr 参数(获取随机字符串)
// 随机串
String nonceStr = UUID.randomUUID().toString();
3.获取 ticket 参数,先获取 access_tocken,然后通过 tocker 参数获取 ticket,获取 tocken 可借鉴 企业微信开发:access_tocken
// 获取 access_token
AccessToken accessToken = AccessTokenAPI.getAccessToken(CORPID,SECRET);
// 根据 tocker 获取 jstl
String urlStr = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token="+accessToken.getToken();
JSONObject jsonObject = HttpRequestUtil.httpRequest(urlStr, EnumMethod.GET.name(), null);
String ticket = jsonObject.getString("ticket");
4.获取 signature 参数,signature 参数是通过“随机字符串+ticket+随机时间戳+当前页面路径”进行 sha1 加密得来的值,这个地方最需要注意的就是你的 url 参数,一定是获取当前页面的 url !!! 并且必须是可信域名,关于可信域名配置可借鉴:企业微信开发:自建应用配置可信域名
String signature = getsig(nonceStr,ticket,timestamp,url);
已使用的工具类
// 使用随机字符串+ticket+随机时间戳+当前页面路径
public static String getsig(String noncestr,String jsapi_ticket,String timestamp,String url){
String[] paramArr = new String[] { "jsapi_ticket=" + jsapi_ticket,
"timestamp=" + timestamp, "noncestr=" + noncestr, "url=" + url };
Arrays.sort(paramArr);
// 将排序后的结果拼接成一个字符串
String content = paramArr[0].concat("&"+paramArr[1]).concat("&"+paramArr[2])
.concat("&"+paramArr[3]);
String gensignature = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
// 对拼接后的字符串进行 sha1 加密
System.out.println("拼接加密签名:"+content.toString());
byte[] digest = md.digest(content.toString().getBytes());
gensignature = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 将 sha1 加密后的字符串与 signature 进行对比
if (gensignature != null) {
return gensignature;// 返回signature
} else {
return "false";
}
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
到这后台的配置就算是完成了,我们看一下完整的后台代码
@Controller
@RequestMapping("/index")
public class TestController {
private String CORPID = "xxxxx";
private String SECRET = "xxxxx";
// 跳转到主页
@RequestMapping(value = "getIndex",method = {RequestMethod.GET,RequestMethod.POST})
public String getIndex( Model model){
Map<String,Object> map = signature(request);
model.addAttribute("appId",map.get("appId"));
model.addAttribute("timestamp",map.get("timestamp"));
model.addAttribute("nonceStr",map.get("nonceStr"));
model.addAttribute("signature",map.get("signature"));
return "index";
}
// 获取签名方法
private Map<String,Object> signature(HttpServletRequest request){
String url = "http://linluochen.cn"+request.getRequestURI();
request.getRequestURL().toString();
Map<String,Object> map = new HashMap<String,Object>();
// 时间戳
String timestamp = Long.toString((new Date().getTime()) / 1000);
// 随机串
String nonceStr = UUID.randomUUID().toString();
// 获取 access_token
AccessToken accessToken = AccessTokenAPI.getAccessToken(CORPID,SECRET);
// 根据 tocker 获取 jstl
String urlStr = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token="+accessToken.getToken();
JSONObject jsonObject = HttpRequestUtil.httpRequest(urlStr, EnumMethod.GET.name(), null);
String ticket = jsonObject.getString("ticket");
System.out.println("要加密的参数:"+nonceStr+" "+ticket+" "+timestamp+" "+url);
String signature = getsig(nonceStr,ticket,timestamp,url);
map.put("appId",CORPID);
map.put("timestamp",timestamp);
map.put("nonceStr",nonceStr);
map.put("signature",signature);
System.out.println("返回结果Map:"+map);
return map;
}
// 使用随机字符串+ticket+随机时间戳+当前页面路径
public static String getsig(String noncestr,String jsapi_ticket,String timestamp,String url){
String[] paramArr = new String[] { "jsapi_ticket=" + jsapi_ticket,
"timestamp=" + timestamp, "noncestr=" + noncestr, "url=" + url };
Arrays.sort(paramArr);
// 将排序后的结果拼接成一个字符串
String content = paramArr[0].concat("&"+paramArr[1]).concat("&"+paramArr[2])
.concat("&"+paramArr[3]);
String gensignature = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
// 对拼接后的字符串进行 sha1 加密
System.out.println("拼接加密签名:"+content.toString());
byte[] digest = md.digest(content.toString().getBytes());
gensignature = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 将 sha1 加密后的字符串与 signature 进行对比
if (gensignature != null) {
return gensignature;// 返回signature
} else {
return "false";
}
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}
最后我们配置一下前台页面的代码(我就直接贴代码片段了,就不整个页面的放了)
wx.config({
debug: false,//生产环境需要关闭debug模式
appId: "[[${appId}]]",//appId通过微信服务号后台查看
timestamp: "[[${timestamp}]]",//生成签名的时间戳
nonceStr: "[[${nonceStr}]]",//生成签名的随机字符串
signature: "[[${signature}]]",//签名
jsApiList: [//需要调用的JS接口列表
'hideOptionMenu', // 隐藏菜单
]
});
wx.ready(function(){
wx.hideOptionMenu(); // 验证成功后调用隐藏菜单的方法
});
测试
测试结果都是大差不差的一样