前言
描述一下大概的应用场景,就是微信的移动端需要拍摄实时图像上传,然后传输到后台,由于我们需要的是直接可以显示的图片,所以要转换成 base 加密后的图片,然后放到 img 标签里进行展示
概述
简单说一下 JS-SDK 的作用吧,微信 JS-SDK 是微信公众平台 面向网页开发者提供的基于微信内的网页开发工具包,可以通过 JS-SDK 使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,要使用 JS-SDK 的第一步就是配置 wx.config 接口,不对第一步是引用 jweixin-1.6.0.js 文件,如果上述文件不可使用就下载这个 jweixin-1.6.0.js ,下边主要讲一下怎么配置 wx.config 接口
配置 wx.config 接口
首先 wx.config 相当于一个总开关吧,也可以说是一个全局对象,只有通过正确的授权验证才可以使用 JS-SDK 所附带的接口,实际上就相当于一个权限验证,验证成功就可以通过 wx.ready 接口实现,验证失败就进入到了 wx.error 接口
wx.config 接口注入权限验证配置
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});
获取第一个参数生成签名的时间戳(其实就是获取当前时间)
// 时间戳
String timestamp = Long.toString((new Date().getTime()) / 1000);
获取第二个签名随机串(也没啥特别的就是 UUID)
// 随机串
String nonceStr = UUID.randomUUID().toString();
获取第三个参数签名!!这个比较重要,签名 = 随机串 + ticket + 时间戳 + URL(当前页面),但是随机串和时间戳我们在上边已经获取到了,下边我们只获取 ticket和当前页面的 URL 就好了,最后把他们拼到一起
/**
获取 signature 的四个步骤
1.获取当前页面 URL
2.获取 access_token 参数
3.通过 access_token 获取 ticket 参数
4.把所有参数拼在一起获取 signature 签名
**/
// 1.url(判断是否有参数如果有参数把参数也拼接上,并且这个路径必须是配置在白名单里边的)
String params = request.getQueryString();
if(StringUtils.isEmpty(params)){
params="";
}else{
params="?"+params;
}
String url = "https://www.baidu.cn"+request.getRequestURI()+params;
// 2. toekn
String access_token = AccessTokenAPI.getAccessToken(appId,appsecret);
// 3.ticket
String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+access_token+"&type=jsapi";
JSONObject jsonObject = HttpRequestUtil.httpRequest(urlStr, EnumMethod.GET.name(), null);
String ticket = jsonObject.getString("ticket");
// 4.signature
String signature = getsig(nonceStr,ticket,timestamp,url);
// 最后放入 map 中传到页面
Map<String,Object> map = new HashMap<String,Object>();
map.put("appId",appId);
map.put("timestamp",timestamp);
map.put("nonceStr",nonceStr);
map.put("signature",signature);
定义 AccessToken 实体类并获取 access_tocken 参数(第二步骤)
// AccessToken 实体类
@Data
public class AccessToken {
// 获取到的凭证
private String token;
// 凭证有效时间,单位:秒
private int expiresIn;
}
/**
* 公众平台通用接口工具类
*
*/
public class AccessTokenAPI {
// 获取微信公众号:access_token的接口地址(GET) 限2000(次/天)
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 获取access_token
*/
public static AccessToken getAccessToken(String appid, String appsecret) {
AccessToken accessToken = null;
String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
JSONObject jsonObject = HttpRequestUtil.httpRequest(requestUrl,EnumMethod.GET.name(), null);
if (jsonObject == null) {
jsonObject = HttpRequestUtil.httpRequest(requestUrl,EnumMethod.GET.name(), null);
}
// 如果请求成功
if (null != jsonObject) {
try {
accessToken = new AccessToken();
accessToken.setToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getInteger("expires_in"));
} catch (JSONException e) {
accessToken = null;
// 获取token失败
}
}
return accessToken;
}
}
拼接 signature 签名的工具类(第四步骤)
private 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);
byte[] digest = md.digest(content.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;
}
第四个 jsApiList 实际上是需要调用的接口名字放进去就好了
完整代码
这是获取 wx.config 授权验证的完整代码
@Service
public class WxConfigUtils {
@Value("${appId}")
private String appId;
@Value("${appSecret}")
private String appSecret;
public Map<String,Object> getWxconfig(HttpServletRequest request){
String params = request.getQueryString();
if(StringUtils.isEmpty(params)){
params="";
}else{
params="?"+params;
}
String url = "https://xxx.xx.cn"+request.getRequestURI()+params;
//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
String access_token = AccessTokenAPI.getAccessToken(appId,appSecret);
// 根据 tocker 获取 jstl
String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+access_token+"&type=jsapi";
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",appId);
map.put("timestamp",timestamp);
map.put("nonceStr",nonceStr);
map.put("signature",signature);
return map;
}
private 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);
byte[] digest = md.digest(content.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;
}
}
前端页面实现
完整的前端界面,就不上传样式文件了,随便写写样式放个图片就 ok 了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>调用企业微信图像上传接口</title>
<!--上传图片css-->
<link rel="stylesheet" href="../css/style.css" />
</head>
<body>
<header class="upload-hedaer">
<a href="javaScript:history.back(-1)" class="upload-fh"></a>
<div>上传</div>
<div>···</div>
</header>
<section class="upload-section">
<article class="upload-piclist">
<div class="upload-file" id="uploadfile"></div>
</article>
</section>
<script src="../js/jweixin-1.6.0.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/jquery-3.0.0.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/wxconfig.js" type="text/javascript" charset="utf-8"></script>
</body>
<script type="text/javascript">
/**
这个地方我们只需要在要使用的页面引用就 OK
1.chooseImage:和拍照或从手机相册中选图接口
2.getLocalImgData:获取本地图片接口
**/
wx.config({
debug: true, // 生产环境需要关闭debug模式
appId: "[[${appId}]]", // appID = corpID 通过微信服务号/企业微信后台查看
timestamp: "[[${timestamp}]]", // 生成签名的时间戳
nonceStr: "[[${nonceStr}]]", // 生成签名的随机字符串
signature: "[[${signature}]]", // 签名
jsApiList: [ // 需要调用的JS接口列表
'chooseImage',
'getLocalImgData'
]
});
</script>
</html>
我们的 wx.ready 接口是单独写了一个名叫 wxconfig.js 的文件,因为考虑到不止一个页面会用到这个功能,所以写成了公共方法
wx.ready(function(){
var piclist = document.getElementsByClassName('upload-piclist')[0];
//给上传图标绑定onclick事件
$("#uploadfile").click(function(){
// 调取接口
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function(res) {
wx.getLocalImgData({
localId: res.localIds[0], // 图片的localID
success: function(res) {
var localData = res.localData;
var imageBase64 = '';
if (localData.indexOf('data:image') == 0) {
//苹果的直接赋值,默认生成'data:image/jpeg;base64,'的头部拼接
imageBase64 = localData;
} else {
//此处是安卓中的唯一得坑!在拼接前需要对localData进行换行符的全局替换
//此时一个正常的 base64 图片路径就完美生成赋值到 img 的 src 中了
imageBase64 = 'data:image/jpeg;base64,' + localData.replace(/\n/g, '');
}
var html = document.createElement('div');
html.innerHTML = '<img id="image" src=' + imageBase64 + ' alt="">'
piclist.appendChild(html);
}
});
}
});
})
});