一、本节要点
1.1可信域名
所有的JS接口只能在企业微信应用的可信域名下调用(包括子域名),可在企业微信的管理后台“我的应用”里设置应用可信域名。这个域名必须要通过ICP备案,不然jssdk会配置失败
1.2JS-SDK使用权限签名算法
1.2.1 签名生成规则如下:
(1)参与签名的字段包括:
noncestr(随机字符串),
有效的jsapi_ticket,
timestamp(时间戳),
url(当前网页的URL,不包含#及其后面部分) 。
(2)对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式 (即 key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
(3)对string1进行sha1签名,得到signature:
1.2.2示例:
(1)待签名参数:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com
(2)字典序
string1=jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-
HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com
(3)sha1加密
signature=sha1(string1)
1.2.3代码示例:
1 /** 2 * 3.获取微信的JSSDK配置信息 3 * @param request 4 * @return 5 */ 6 public static Map<String, Object> getWxConfig(HttpServletRequest request) { 7 Map<String, Object> ret = new HashMap<String, Object>(); 8 //1.准备好参与签名的字段 9 10 String nonceStr = UUID.randomUUID().toString(); // 必填,生成签名的随机串 11 //System.out.println("nonceStr:"+nonceStr); 12 String accessToken=WeiXinUtil.getAccessToken(WeiXinParamesUtil.corpId, WeiXinParamesUtil.agentSecret).getToken(); 13 String jsapi_ticket =getJsapiTicket(accessToken);// 必填,生成签名的H5应用调用企业微信JS接口的临时票据 14 //System.out.println("jsapi_ticket:"+jsapi_ticket); 15 String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成签名的时间戳 16 //System.out.println("timestamp:"+timestamp); 17 String url=request.getRequestURL().toString(); 18 //System.out.println("url:"+url); 19 20 //2.字典序 ,注意这里参数名必须全部小写,且必须有序 21 String sign = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonceStr+ "×tamp=" + timestamp + "&url=" + url; 22 23 //3.sha1签名 24 String signature = ""; 25 try { 26 MessageDigest crypt = MessageDigest.getInstance("SHA-1"); 27 crypt.reset(); 28 crypt.update(sign.getBytes("UTF-8")); 29 signature = byteToHex(crypt.digest()); 30 //System.out.println("signature:"+signature); 31 } catch (NoSuchAlgorithmException e) { 32 e.printStackTrace(); 33 } catch (UnsupportedEncodingException e) { 34 e.printStackTrace(); 35 } 36 ret.put("appId", WeiXinParamesUtil.corpId); 37 ret.put("timestamp", timestamp); 38 ret.put("nonceStr", nonceStr); 39 ret.put("signature", signature); 40 return ret; 41 } 42 43 44 /** 45 * 方法名:byteToHex</br> 46 * 详述:字符串加密辅助方法 </br> 47 * 开发人员:souvc </br> 48 * 创建时间:2016-1-5 </br> 49 * @param hash 50 * @return 说明返回值含义 51 * @throws 说明发生此异常的条件 52 */ 53 private static String byteToHex(final byte[] hash) { 54 Formatter formatter = new Formatter(); 55 for (byte b : hash) { 56 formatter.format("%02x", b); 57 } 58 String result = formatter.toString(); 59 formatter.close(); 60 return result; 61 62 } 63 64 65 66 private static String getExt(String contentType){ 67 if("image/jpeg".equals(contentType)){ 68 return ".jpg"; 69 }else if("image/png".equals(contentType)){ 70 return ".png"; 71 }else if("image/gif".equals(contentType)){ 72 return ".gif"; 73 } 74 75 return null; 76 }
二、代码实现
2.1 配置可信域名
在登录企业微信后台,配置应用:企业应用->自建应用->选择你的应用->网页授权及JS-SDK->输入你的域名。
这样安全域名就配置好了。
2.2 JSSDK的前端页面—JSSDKUploadPics.jsp
此页面完整代码:
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@page language="java" import="com.ray.util.WeiXinUtil"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>上传报销单</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <script src="js/jquery-3.2.1.min.js"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <style type="text/css"> html { -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; -webkit-user-select: none; user-select: none; } body { line-height: 1.6; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; background-color: #f1f0f6; } * { margin: 0; padding: 0; } button { font-family: inherit; font-size: 100%; margin: 0; *font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } ul, ol { padding-left: 0; list-style-type: none; } a { text-decoration: none; } .label_box { background-color: #ffffff; } .label_item { padding-left: 15px; } .label_inner { padding-top: 10px; padding-bottom: 10px; min-height: 24px; position: relative; } .label_inner:before { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); top: auto; bottom: -2px; } .lbox_close { position: relative; } .lbox_close:before { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); } .lbox_close:after { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); top: auto; bottom: -2px; } .lbox_close .label_item:last-child .label_inner:before { display: none; } .btn { display: block; margin-left: auto; margin-right: auto; padding-left: 14px; padding-right: 14px; font-size: 18px; text-align: center; text-decoration: none; overflow: visible; /*.btn_h(@btnHeight);*/ height: 42px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; color: #ffffff; line-height: 42px; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } .btn.btn_inline { display: inline-block; } .btn_primary { background-color: #437DBA; } .btn_primary:not (.btn_disabled ):visited { color: #ffffff; } .btn_primary:not (.btn_disabled ):active { color: rgba(255, 255, 255, 0.9); background-color: #3b78b9; } button.btn { width: 100%; border: 0; outline: 0; -webkit-appearance: none; } button.btn:focus { outline: 0; } .wxapi_container { font-size: 16px; } h1 { font-size: 14px; font-weight: 400; line-height: 2em; padding-left: 15px; color: #8d8c92; } .desc { font-size: 14px; font-weight: 400; line-height: 2em; color: #8d8c92; } .wxapi_index_item a { display: block; color: #3e3e3e; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } .wxapi_form { background-color: #ffffff; padding: 0 15px; margin-top: 30px; padding-bottom: 15px; } h3 { padding-top: 16px; margin-top: 25px; font-size: 16px; font-weight: 400; color: #3e3e3e; position: relative; } h3:first-child { padding-top: 15px; } h3:before { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); } .btn { margin-bottom: 15px; } </style> </head> <body> <% Map<String, Object> res = new HashMap<String, Object>(); res = WeiXinUtil.getWxConfig(request); request.setAttribute("appId", res.get("appId")); request.setAttribute("timestamp", res.get("timestamp")); request.setAttribute("nonceStr", res.get("nonceStr")); request.setAttribute("signature", res.get("signature")); %> <body> <div class="wxapi_container"> <form action="" method="POST"></form> <div class="lbox_close wxapi_form"> <h3 id="menu-basic">基础接口</h3> <span class="desc">判断当前客户端是否支持指定JS接口</span> <button class="btn btn_primary" id="checkJsApi">checkJsApi</button> <span class="desc">上传图片接口</span> <button class="btn btn_primary" id="uploadImage">uploadImage</button> <span class="desc">下载图片接口</span> <button class="btn btn_primary" id="downloadImage">downloadImage</button> <span class="desc">调起微信扫一扫接口</span> <button class="btn btn_primary" id="scanQRCode1">scanQRCode(直接返回结果)</button> <span class="desc">测试按钮</span> <button class="btn btn_primary" id="ceshi">ceshi</button> </div> </div> <script> /* * 注意: * 所有的JS接口只能在应用配置的安全域名下面使用。 * */ wx.config({ beta : true, debug : true, appId : ‘${appId}‘, timestamp : ‘${timestamp}‘, nonceStr : ‘${nonceStr }‘, signature : ‘${signature}‘, jsApiList : [ ‘checkJsApi‘, ‘chooseImage‘, ‘previewImage‘, ‘uploadImage‘, ‘downloadImage‘, ‘scanQRCode‘, ] }); //通过ready接口处理成功验证 wx.ready(function() { // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 $("#ceshi").click(function() { alert("ceshi11111111"); }); }); // 1 判断当前版本是否支持指定 JS 接口,支持批量判断 $("#checkJsApi").click(function() { wx.checkJsApi({ jsApiList : [ ‘getNetworkType‘, ‘previewImage‘ ], success : function(res) { alert(JSON.stringify(res)); } }); }); //2.拍照或从手机相册中选图接口 var images = { localId : [], serverId : [] }; $("#uploadImage").click(function() { wx.chooseImage({ success : function(res) { images.localId = res.localIds; alert(‘已选择 ‘ + res.localIds.length + ‘ 张图片‘); uploadImg(); } }); }); // 5.3 上传图片 function uploadImg() { if (images.localId.length == 0) { alert(‘请先使用 chooseImage 接口选择图片‘); return; } var i = 0, length = images.localId.length; images.serverId = []; function upload() { wx .uploadImage({ localId : images.localId[i], success : function(res) { i++; alert(‘已上传:‘ + i + ‘/‘ + length); images.serverId.push(res.serverId); //将serverId上传至服务器 alert("ajax请求即将执行--"); $ .ajax({ type : "POST", url : "http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/uploadExpenseAccaoutServlet", data : { serverId : res.serverId }, dataType : "text", success : function(data) { alert(data); } }); if (i < length) { upload(); } }, fail : function(res) { alert(JSON.stringify(res)); } }); } upload(); }; //点击扫描按钮,扫描二维码并返回结果 document.querySelector(‘#scanQRCode1‘).onclick = function() { wx .scanQRCode({ desc : ‘scanQRCode desc‘, needResult : 1, success : function(res) { //扫码后获取结果参数:htpp://xxx.com/c/?6123,截取到url中的防伪码后,赋值给Input var result = res.resultStr; alert(result); $ .ajax({ type : "POST", url : "http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/qrservlet", data : { result : res.resultStr }, dataType : "text", success : function(data) { alert(data); } }); } }); }; </script> </body> </html>
此页面主要包括:
(1)引入JS文件:
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js
<script src="js/jquery-3.2.1.min.js"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
(2)调用后台WeiXinUtil.getWxConfig(HttpServletRequest request)方法,获取企业微信的JSSDK配置信息
<% Map<String, Object> res = new HashMap<String, Object>(); res = WeiXinUtil.getWxConfig(request); request.setAttribute("appId", res.get("appId")); request.setAttribute("timestamp", res.get("timestamp")); request.setAttribute("nonceStr", res.get("nonceStr")); request.setAttribute("signature", res.get("signature")); %>
(3)通过config接口注入权限验证配置
wx.config({ beta : true, debug : true, appId : ‘${appId}‘, timestamp : ‘${timestamp}‘, nonceStr : ‘${nonceStr }‘, signature : ‘${signature}‘, jsApiList : [ ‘checkJsApi‘, ‘chooseImage‘, ‘previewImage‘, ‘uploadImage‘, ‘downloadImage‘, ‘scanQRCode‘, ] });
(4)选择图片与图片上传,以及通过ajax调用后台servlet
//2.拍照或从手机相册中选图接口 var images = { localId : [], serverId : [] }; $("#uploadImage").click(function() { wx.chooseImage({ success : function(res) { images.localId = res.localIds; alert(‘已选择 ‘ + res.localIds.length + ‘ 张图片‘); uploadImg(); } }); }); // 5.3 上传图片 function uploadImg() { if (images.localId.length == 0) { alert(‘请先使用 chooseImage 接口选择图片‘); return; } var i = 0, length = images.localId.length; images.serverId = []; function upload() { wx .uploadImage({ localId : images.localId[i], success : function(res) { i++; alert(‘已上传:‘ + i + ‘/‘ + length); images.serverId.push(res.serverId); //将serverId上传至服务器 alert("ajax请求即将执行--"); $ .ajax({ type : "POST", url : "http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/uploadExpenseAccaoutServlet", data : { serverId : res.serverId }, dataType : "text", success : function(data) { alert(data); } }); if (i < length) { upload(); } }, fail : function(res) { alert(JSON.stringify(res)); } }); } upload(); };
2.3 获取企业微信JSSDK配置信息—WeiXinUtil.java
此类完整代码:
package com.ray.util; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ray.pojo.AccessToken; import net.sf.json.JSONException; import net.sf.json.JSONObject; public class WeiXinUtil { private static Logger log = LoggerFactory.getLogger(WeiXinUtil.class); //微信的请求url //获取access_token的接口地址(GET) 限200(次/天) public final static String access_token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={corpsecret}"; //获取jsapi_ticket的接口地址(GET) 限200(次/天) public final static String jsapi_ticket_url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESSTOKEN"; /** * 1.发起https请求并获取结果 * * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; StringBuffer buffer = new StringBuffer(); try { // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 设置请求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // 当有数据需要提交时 if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // 注意编码格式,防止中文乱码 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 将返回的输入流转换成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("Weixin server connection timed out."); } catch (Exception e) { log.error("https request error:{}", e); } return jsonObject; } /** * 2.发送https请求之获取临时素材 * @param requestUrl * @param savePath 文件的保存路径,此时还缺一个扩展名 * @return * @throws Exception */ public static File getFile(String requestUrl,String savePath) throws Exception { //String path=System.getProperty("user.dir")+"/img//1.png"; // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 设置请求方式(GET/POST) httpUrlConn.setRequestMethod("GET"); httpUrlConn.connect(); //获取文件扩展名 String ext=getExt(httpUrlConn.getContentType()); savePath=savePath+ext; System.out.println("savePath"+savePath); //下载文件到f文件 File file = new File(savePath); // 获取微信返回的输入流 InputStream in = httpUrlConn.getInputStream(); //输出流,将微信返回的输入流内容写到文件中 FileOutputStream out = new FileOutputStream(file); int length=100*1024; byte[] byteBuffer = new byte[length]; //存储文件内容 int byteread =0; int bytesum=0; while (( byteread=in.read(byteBuffer)) != -1) { bytesum += byteread; //字节数 文件大小 out.write(byteBuffer,0,byteread); } System.out.println("bytesum: "+bytesum); in.close(); // 释放资源 out.close(); in = null; out=null; httpUrlConn.disconnect(); return file; } /** * @desc :2.微信上传素材的请求方法 * * @param requestUrl 微信上传临时素材的接口url * @param file 要上传的文件 * @return String 上传成功后,微信服务器返回的消息 */ public static String httpRequest(String requestUrl, File file) { StringBuffer buffer = new StringBuffer(); try{ //1.建立连接 URL url = new URL(requestUrl); HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection(); //打开链接 //1.1输入输出设置 httpUrlConn.setDoInput(true); httpUrlConn.setDoOutput(true); httpUrlConn.setUseCaches(false); // post方式不能使用缓存 //1.2设置请求头信息 httpUrlConn.setRequestProperty("Connection", "Keep-Alive"); httpUrlConn.setRequestProperty("Charset", "UTF-8"); //1.3设置边界 String BOUNDARY = "----------" + System.currentTimeMillis(); httpUrlConn.setRequestProperty("Content-Type","multipart/form-data; boundary="+ BOUNDARY); // 请求正文信息 // 第一部分: //2.将文件头输出到微信服务器 StringBuilder sb = new StringBuilder(); sb.append("--"); // 必须多两道线 sb.append(BOUNDARY); sb.append("\r\n"); sb.append("Content-Disposition: form-data;name=\"media\";filelength=\"" + file.length() + "\";filename=\""+ file.getName() + "\"\r\n"); sb.append("Content-Type:application/octet-stream\r\n\r\n"); byte[] head = sb.toString().getBytes("utf-8"); // 获得输出流 OutputStream outputStream = new DataOutputStream(httpUrlConn.getOutputStream()); // 将表头写入输出流中:输出表头 outputStream.write(head); //3.将文件正文部分输出到微信服务器 // 把文件以流文件的方式 写入到微信服务器中 DataInputStream in = new DataInputStream(new FileInputStream(file)); int bytes = 0; byte[] bufferOut = new byte[1024]; while ((bytes = in.read(bufferOut)) != -1) { outputStream.write(bufferOut, 0, bytes); } in.close(); //4.将结尾部分输出到微信服务器 byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线 outputStream.write(foot); outputStream.flush(); outputStream.close(); //5.将微信服务器返回的输入流转换成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); } catch (IOException e) { System.out.println("发送POST请求出现异常!" + e); e.printStackTrace(); } return buffer.toString(); } /** * 2.发起http请求获取返回结果 * * @param requestUrl 请求地址 * @return */ public static String httpRequest(String requestUrl) { StringBuffer buffer = new StringBuffer(); try { URL url = new URL(requestUrl); HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection(); httpUrlConn.setDoOutput(false); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); httpUrlConn.setRequestMethod("GET"); httpUrlConn.connect(); // 将返回的输入流转换成字符串 InputStream inputStream = httpUrlConn.getInputStream(); //InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); } catch (Exception e) { } return buffer.toString(); } /** * 3.获取access_token * * @param appid 凭证 * @param appsecret 密钥 * @return */ public static AccessToken getAccessToken(String appid, String appsecret) { AccessToken accessToken = null; String requestUrl = access_token_url.replace("{corpId}", appid).replace("{corpsecret}", appsecret); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); // 如果请求成功 if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setToken(jsonObject.getString("access_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in")); } catch (JSONException e) { accessToken = null; // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return accessToken; } /** * 4. 获取JsapiTicket * @param accessToken * @return */ public static String getJsapiTicket(String accessToken){ String requestUrl = jsapi_ticket_url.replace("ACCESSTOKEN", accessToken); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); String jsapi_ticket=""; // 如果请求成功 if (null != jsonObject) { try { jsapi_ticket=jsonObject.getString("ticket"); } catch (JSONException e) { // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return jsapi_ticket; } /** * 3.获取企业微信的JSSDK配置信息 * @param request * @return */ public static Map<String, Object> getWxConfig(HttpServletRequest request) { Map<String, Object> ret = new HashMap<String, Object>(); //1.准备好参与签名的字段 String nonceStr = UUID.randomUUID().toString(); // 必填,生成签名的随机串 //System.out.println("nonceStr:"+nonceStr); String accessToken=WeiXinUtil.getAccessToken(WeiXinParamesUtil.corpId, WeiXinParamesUtil.agentSecret).getToken(); String jsapi_ticket =getJsapiTicket(accessToken);// 必填,生成签名的H5应用调用企业微信JS接口的临时票据 //System.out.println("jsapi_ticket:"+jsapi_ticket); String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成签名的时间戳 //System.out.println("timestamp:"+timestamp); String url=request.getRequestURL().toString(); //System.out.println("url:"+url); //2.字典序 ,注意这里参数名必须全部小写,且必须有序 String sign = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonceStr+ "×tamp=" + timestamp + "&url=" + url; //3.sha1签名 String signature = ""; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(sign.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); //System.out.println("signature:"+signature); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put("appId", WeiXinParamesUtil.corpId); ret.put("timestamp", timestamp); ret.put("nonceStr", nonceStr); ret.put("signature", signature); return ret; } /** * 方法名:byteToHex</br> * 详述:字符串加密辅助方法 </br> * 开发人员:souvc </br> * 创建时间:2016-1-5 </br> * @param hash * @return 说明返回值含义 * @throws 说明发生此异常的条件 */ private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } private static String getExt(String contentType){ if("image/jpeg".equals(contentType)){ return ".jpg"; }else if("image/png".equals(contentType)){ return ".png"; }else if("image/gif".equals(contentType)){ return ".gif"; } return null; } }
获取获取企业微信JSSDK配置信息的方法为:
public static Map<String, Object> getWxConfig(HttpServletRequest request)
2.4创建跳转菜单按钮
完整代码:
MenuService.java
package com.ray.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.ray.pojo.menu.Button; import com.ray.pojo.menu.CommonButton; import com.ray.pojo.menu.ComplexButton; import com.ray.pojo.menu.Menu; import com.ray.pojo.menu.ViewButton; import com.ray.util.WeiXinUtil; import net.sf.json.JSONObject; public class MenuService { private static Logger log = LoggerFactory.getLogger(MenuService.class); // 菜单创建(POST) 限100(次/天) public static String create_menu_url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN&agentid=AGENTID"; /** * 1.创建菜单 * * @param menu 菜单实例 * @param accessToken 有效的access_token * @return 0表示成功,其他值表示失败 */ public void createMenu(String accessToken,Menu menu,int agentId) { //1.获取json字符串:将Menu对象转换为json字符串 Gson gson = new Gson(); String jsonMenu =gson.toJson(menu); //使用gson.toJson(user)即可将user对象顺序转成json System.out.println("jsonMenu:"+jsonMenu); //2.获取请求的url create_menu_url = create_menu_url.replace("ACCESS_TOKEN", accessToken) .replace("AGENTID", String.valueOf(agentId)); //3.调用接口,发送请求,创建菜单 JSONObject jsonObject = WeiXinUtil.httpRequest(create_menu_url, "POST", jsonMenu); System.out.println("jsonObject:"+jsonObject.toString()); //4.错误消息处理 if (null != jsonObject) { if (0 != jsonObject.getInt("errcode")) { log.error("创建菜单失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } } /** * 2.组装菜单数据 * * @return */ public Menu getMenu() { /* ViewButton btn11 = new ViewButton(); btn11.setName("添加报销单"); btn11.setType("view"); btn11.setUrl("http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/uploadExpenseAccaout.jsp"); */ ViewButton btn11 = new ViewButton(); btn11.setName("JSSDK多图上传"); btn11.setType("view"); btn11.setUrl("http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/JSSDKUploadPics.jsp"); ViewButton btn21 = new ViewButton(); btn21.setName("JSSDK测试(全)"); btn21.setType("view"); btn21.setUrl("http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/jsapiTicktAll.jsp"); ViewButton btn22 = new ViewButton(); btn22.setName("PC端网页授权"); btn22.setType("view"); btn22.setUrl("https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=ww92f5da92bb24696e&agentid=1000002&redirect_uri=http%3A%2F%2F5nffqn.natappfree.cc%2FWeiXin_QiYe_Demo%2Fwebauthorization.jsp&state=state"); CommonButton btn12 = new CommonButton(); btn12.setName("扫一扫"); btn12.setType("click"); btn12.setKey("12"); CommonButton btn13 = new CommonButton(); btn13.setName("翻译功能"); btn13.setType("click"); btn13.setKey("13"); ViewButton btn14 = new ViewButton(); btn14.setName("上传图片"); btn14.setType("view"); btn14.setUrl("http://5nffqn.natappfree.cc/WeiXin_SanFenBaiXue/uploadimg.jsp"); ViewButton btn15 = new ViewButton(); btn15.setName("上传图片2"); btn15.setType("view"); btn15.setUrl("http://5nffqn.natappfree.cc/WeiXin_SanFenBaiXue/index2.jsp"); CommonButton btn23 = new CommonButton(); btn23.setName("美女电台"); btn23.setType("click"); btn23.setKey("23"); CommonButton btn24 = new CommonButton(); btn24.setName("人脸识别"); btn24.setType("click"); btn24.setKey("24"); CommonButton btn25 = new CommonButton(); btn25.setName("聊天唠嗑"); btn25.setType("click"); btn25.setKey("25"); CommonButton btn31 = new CommonButton(); btn31.setName("Q友圈"); btn31.setType("click"); btn31.setKey("31"); CommonButton btn33 = new CommonButton(); btn33.setName("幽默笑话"); btn33.setType("click"); btn33.setKey("33"); CommonButton btn34 = new CommonButton(); btn34.setName("用户反馈"); btn34.setType("click"); btn34.setKey("34"); CommonButton btn35 = new CommonButton(); btn35.setName("关于我们"); btn35.setType("click"); btn35.setKey("35"); ViewButton btn32 = new ViewButton(); btn32.setName("周边搜索"); btn32.setType("view"); btn32.setUrl("http://liufeng.gotoip2.com/xiaoqrobot/help.jsp"); ComplexButton mainBtn1 = new ComplexButton(); mainBtn1.setName("正在做功能"); mainBtn1.setSub_button(new Button[] { btn11, btn12, btn13, btn14, btn15 }); ComplexButton mainBtn2 = new ComplexButton(); mainBtn2.setName("测试"); mainBtn2.setSub_button(new Button[] { btn21, btn22, btn23, btn24, btn25 }); ComplexButton mainBtn3 = new ComplexButton(); mainBtn3.setName("更多"); mainBtn3.setSub_button(new Button[] { btn31, btn33, btn34, btn35, btn32 }); /** * 这是企业号目前的菜单结构,每个一级菜单都有二级菜单项<br> * * 在某个一级菜单下没有二级菜单的情况,menu该如何定义呢?<br> * 比如,第三个一级菜单项不是“更多体验”,而直接是“幽默笑话”,那么menu应该这样定义:<br> * menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 }); */ Menu menu = new Menu(); menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 }); return menu; } }
MenuTest.java
package com.ray.test; import org.junit.Test; import com.ray.pojo.menu.Menu; import com.ray.service.MenuService; import com.ray.util.WeiXinParamesUtil; import com.ray.util.WeiXinUtil; public class MenuTest { @Test public void testCreateMenu(){ //1.组装菜单 MenuService ms=new MenuService(); Menu menu=ms.getMenu(); //2.获取access_token:根据企业id和应用密钥获取access_token String accessToken=WeiXinUtil.getAccessToken(WeiXinParamesUtil.corpId, WeiXinParamesUtil.agentSecret).getToken(); System.out.println("accessToken:"+accessToken); //3.创建菜单 ms.createMenu( accessToken, menu, WeiXinParamesUtil.agentId); } }
创建跳转菜单按钮,点击后,跳转到 JSSDKUploadPics.jsp页面
(1)在MenuService.java中添加一个跳转按钮。
ViewButton btn11 = new ViewButton(); btn11.setName("JSSDK多图上传"); btn11.setType("view"); btn11.setUrl("http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/JSSDKUploadPics.jsp");
(2)运行MenuTest类中的testCreateMenu,来创建菜单。
2.5 总结一下JSSDK完整过程
这样JSSDK完整过程如下:
(1)点击菜单按钮跳转到JSSDKUploadPics.jsp页面
(2)调用后台方法获取微信配置信息
(3)通过config接口注入(2)中获取的权限验证配置
(4)弹出JSSDK配置成功的提示框
(5)选择图片并上传到微信服务器
(6)上传图片到微信服务器后,将微信服务器返回的图片的serverID(即mediaId)通过ajax方式传到后台servlet
(7)在servlet中,根据接收的serverID,进行获取临时素材并存到本地的操作(这一步请见下一节)
至此JSSDK的配置已经成功,我们可以调用微信JSSDK提供的众多接口了。