最近在搞企业微信的东西,刚开始对这个的确没有任何的概念,属于两眼抓瞎的类型,因为场景比较特殊网上搜到的资料也不多,只能自己看着官方文档一点一点去调试。于是,一系列的踩坑之路就上演了,这里就简单介绍一下笔者自己踩的坑吧,也为其他刚刚接触企业微信开发者朋友们提供一点借鉴。
首先,要明确两个概念,就是微信和企业微信不是一个东西(虽然有些信息互通)、企业微信应用和服务商的第三方应用也不是一个东西(虽然也有些接口可以调用),企业内部开发指的是开发某个企业自己用的应用,而第三方应用开发指的就是开发者作为服务商开发第三方应用,让其他企业(使用企业微信的人)安装你(服务商)开发的应用。此处重点介绍下笔者作为服务商第三方应用的开发过程吧!(如下图是企业微信官方文档,上面有企业内部开发和第三方应用开发)
需要的准备:注册两个企业,一个作为应用的服务商,一个作为应用的使用者,使用服务商的企业登陆到服务商的管理平台。此处需要注意的是,企业微信有两个管理平台,一个是企业微信管理后台另一个是服务商管理平台,不要登陆错了。
企业微信管理后台:https://work.weixin.qq.com/wework_admin
企业微信服务商管理后台:https://open.work.weixin.qq.com/wwopen/developer
第一步,在服务商平台创建一个应用,将需要填写的信息填入。基础信息可以根据实际情况填写,开发信息可能刚接触的时候并不清楚如何填写,这里可以暂时先写随便写一个,后面可以修改并且需要认证,下文会将用到的一一进行介绍。
第二步,信息介绍
1.基本信息,在基本信息中尤其要注意的是suiteId和secret,suiteId是用于标记用于的唯一标识,secret是用于加解密的秘钥,尤其是secret,千万不能泄露。
2.使用配置,应用主页是企业安装第三方应用之后,跳转的主页页面;可信域名是企业微信进行授权和前端使用JS-SDK的时候校验的域名,若是域名不一致企业微信会报错;安装完成回调域名是在企业进行安装应用之后回调接口的域名;业务设置URL笔者没有用到,没有进行深究
3.回调配置,数据回调URL笔者没有用到,也是随便写了一个;指令回调URL,这个千万要注意,在后面获取到suite_ticket(应用ticket)的时候就需要用他;Token和EncodingAESKey,自动生成填上就行,不过不要随便去更改,因为这边更改的话,后面解密信息的时候就需要更改代码中的这两个值。
比较重要的信息就是这些,因为笔者自己的需要这边没有用到业务设置URL和数据回调URL,若是有大神用到也希望不吝指出,这里感激不尽。
第三步,开发文档,将以上信息了解了之后,还需要看下官方文档。因为笔者是作为java开发的角度来进行开发的,所以主要需要了解的是快速入门和服务端开发API。注意,此处文档区分第三方应用开发和企业内部开发,服务商开发第三方应用一定是第三方应用的那一篇文档,千万不要看错了!
文档链接:https://work.weixin.qq.com/api/doc
1.快速开发,注意此处的三个场景:企业的、应用的和服务商的。在开发过程中一定要分清这三者的区别,这是重中之重,“企业的”是指登录者所属的企业;“应用的”是指开发者的第三方应用;“服务商的”是指应用所属的企业,这和登录的企业不一样,一定要区分清楚!(此处不分清楚,后面会有很多坑)
2.服务端API,这里就是主要获取需要的信息api都有,但此处要区分清楚应用的、企业的、服务商的、个人的信息,它们不可通用(如下文介绍的user_ticket和suite_ticket不是一个东西)
第四步:指令回调流程的建立,在第二步中说到指令回调Url,需要在这里进行配置(代码在这一步最后贴上),企业微信官方会每10分钟调用一次,或者手动点击“刷新Ticket”,或者在编辑完成确认的时候会进行回调。
指令回调的需要先参考这篇文档(其中有好几个需要注意的点)
注意点1:解密此处有demo,直接使用官方的即可
注意点2:在使用java解密过程中,可能会出现异常java.security.InvalidKeyException:illegal Key Size,这是因为jdk本身有解密策略的限制(有限制的解密策略,好像是为了传输安全?),这需要去oracle官方下载无限制的解密策略,企业微信官方也给出了关于这个问题的解决方案(在解密demo代码的WXBizMsgCrypt类的注释中,如图)
此处附上下载地址:
JDK7:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
注意点3:验证回调和实际数据传输方式不同:验证是GET请求,解密后需要将明文的echostr返回回去,实际数据传输是POST请求,入参是xml格式传入(但是在链接上也会有解密需要的参数msg_signature、timestamp、nonce因此都要接收),需要返回success
注意点4:此处数据类型可能有很多种,具体可以参考文档的这边,此处笔者只是接收了suite_ticket(应用ticket)类型进行处理
以下是笔者自己写的demo的代码(此处用dom4j解析xml字符串):
@ResponseBody@RequestMapping(value = "/directCallback")public void directCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {System.out.println("----------------------------");System.out.println("this is directCallback");response.setCharacterEncoding("UTF-8");try {String corpid = request.getParameter("corpid");// 企业微信加密签名String msgSignature = request.getParameter("msg_signature");// 时间戳 与nonce结合使用,用于防止请求重放攻击String timestamp = request.getParameter("timestamp");// 校验时字符串String echostr = request.getParameter("echostr");// 随机数 与timestamp结合使用,用于防止请求重放攻击String nonce = request.getParameter("nonce");System.out.println("msgSignature: "+ msgSignature);System.out.println("timestamp: "+ timestamp);System.out.println("echostr: "+ echostr);System.out.println("nonce: "+ nonce);if("GET".equals(request.getMethod())) { // get请求表示是验证System.out.println("corpid:" + corpid);String echostrDecrypt = null;// 校验服务商公司idWXBizMsgCrypt wxcpt = new WXBizMsgCrypt(sToken, sEncodingAESKey, corpid);echostrDecrypt = wxcpt.VerifyURL(msgSignature, timestamp, nonce, echostr);System.out.println("verifyurl echostr: " + echostrDecrypt);response.setCharacterEncoding("UTF-8");response.getWriter().write(echostrDecrypt);}else { // post请求表示是真实数据// 获取传过来的xml信息(密文)InputStream ins = request.getInputStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(ins));StringBuilder postData = new StringBuilder();String line = null;while((line=bufferedReader.readLine()) != null) {postData.append(line);}System.out.println("postData:"+ postData);WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(sToken, sEncodingAESKey, suiteid);String info = wxcpt.DecryptMsg(msgSignature, timestamp, nonce, postData.toString());System.out.println("解析的明文是:" + info); // 此处明文是xml信息// 解析xml,若InfoType是suite_ticket的话,说明是发送ticket的响应方式Document doc = DocumentHelper.parseText(info);Element root = doc.getRootElement();String infoType = root.elementTextTrim("InfoType");if("suite_ticket".equals(infoType)) { // 此处只是进行suite_ticket的处理,若是需要其他的处理需要在这里添加类型String suiteTicket = root.elementTextTrim("SuiteTicket");System.out.println("拿到的ticket是: " + suiteTicket);}response.getWriter().write("success");}System.out.println("----------------------------");} catch (Exception e) {//验证URL失败,错误原因请查看异常e.printStackTrace();}}
第五步:建立应用授权流程(注意主体是应用),此处不是登陆者登录之后授权才能用那个流程,而是在安装应用时候的授权流程。此时是为了获取应用的access_token(第三方应用凭证)和permanent_code(永久授权码),有了access_token和permanent_code,加上之前的suite_ticket(应用ticket),很多其他的信息就可以获取到了
1.在服务商网站配置ip访问白名单,这一点不可忽略,否则无法访问
2.获取企业的access_token(第三方应用凭证),此处ticket就是企业微信官方每10分钟推送的suite_ticket(应用ticket),还需要应用的id和应用的secret
3.安装应用获取auth_code(临时授权码),此处只是进行安装测试,若需要正式上线需要走上线流程
使用另一个企业进行此处的安装测试之后,之前实现的回调接口会接收到临时授权码的信息
4.获取到permanent_code永久授权码
有了access_token、permanent_code和suite_ticket之后,很多的信息都可以获取到了
第六步:用户登录时的用户授权流程,在用户登录的时候,需要用户授权应用,获取去到用户信息,将用户与第三方应用中的用户关联起来。此处,企业微信官方采用的是oauth2授权方式,oauth2授权流程可以参考文档此处--------此处请注意下后面的更新,有神坑!!!!!
1.构造oauth2授权链接,此处若是后面需要获取到用户的手机号、邮箱等敏感信息,必须要使用手动授权方式(即scope=snsapi_privateinfo),服务商管理后台中创建的应用,必须支持获取用户的敏感信息,这两个条件只要有一个不满足就无法获取到用户的手机号等敏感信息。
构造的oauth2链接可以放在前端,也可以由后端配置后返回给前端,但是oauth2链接必须由前端跳转(不能由后端重定向,当然内部转发跳不到服务器之外的地址更不行),因为此处关联了设置的可信域名,可信域名关联前端使用的JS-SDK因此必须填前端域名
2.获取user_ticket(用户ticket),此处不能搞混user_ticket和suite_ticket,一个是用户的ticket,通过code获取;一个是应用的ticket,企业微信每10分钟推送给回调函数(笔者就搞混了,一直以为是哪里获取的不对导致获取不到用户信息)
3.若是还需要获取到用户的敏感信息,则根据access_token和user_ticket就可以拿到了,当然,是建立在可以拿到的前提下(应用允许获取、oauth2采用手动授权、用户同意授权)
获取到用户信息之后,就可以将企业微信的用户信息和业务的用户信息关联起来,主要的流程就可以疏通了!
第七步:服务端配置客户端需要的签名signature等信息,在客户端的JS-SDK中,必须要先调用wx.config接口来进行权限的校验之后,才能够进行JS其他接口的调用,此时就需要服务端将签名signature、时间戳timestamp、随机串nonceStr、登录公司corpId返回,如下图
此处需要注意的点不少,只要有一点对不上,那么就可能造成流程走不下去。
注意点1:
使用JSSDK之前,一定要填写可信域名并且校验,否则JSSDK的接口无法使用。此处需要注意的点是,若是测试授权应用的话,进行验证之后,一定要重新安装才能生效,笔者就因为没有重新安装卡了很久,一直以为是配置的问题。
注意点2:
生成签名的算法需要参考客户端附录中的这篇文档(如图),由于之前第三方应用凭证已经获取到,此处jsapi_ticket也可以拿到了
注意点3:
生成签名算法的时候,需要的url参数一定不要加上#后面的参数
注意点4:
校验签名算法工具:https://work.weixin.qq.com/api/jsapisign
此处只能校验签名是否正确,但是不能保证传入的url正确,因此需要确保传入的url包含“?”后面的参数但是不包含#后面的参数,如“http://xxx.xxx.xxx/xxx/?test=xxx”,可使用UUID生成nonceStr,再加上时间戳和url,将代码生成的signature和工具生成的signature进行校对保证生成算法的正确性
注意点5:
在附录最后一篇中,有各种错误的结局方案,但是此处说要将生成signature进行转码,但是亲测转码之后是不行的,不转码可以
以上就是笔者疏通整个企业微信流程的踩过的坑,当然因为也是第一次接触,还有很多还不熟悉,可能还有很多谬误,若是读者发现那里不对,还望不吝指教!
20190814更新
更新遇到的两个神坑:
神坑1.官方文档上提供了获取登录用户的手机号、邮箱等敏感信息
在构建oauth链接的时候,仅仅有一个“已废弃”的字样,可是在使用中还是可以继续使用的,到这里,一切还没有其他问题,正常使用。。。
于是!!!!!在上线审核的时候,获取用户敏感信息的应用,是审核不通过的!!!
找客服仔细确认之后(普通客服到技术客服到产品客服),才知道现在以后不支持获取用户敏感信息的应用上线的!!!!!(可能以后会支持,但是目前不支持,时间节点是20190814)
神坑2.在服务商的应用中,是可以填写多个可信域名的(3个),但是亲测,只有第一个有效!!!另外的两个无论是构建oauth链接,还是使用jssdk(此处笔者调用的是wx.config这个jssdk无效,报错为not reliable domain),均无效(也是找客服确认后才知道,只有第一个是有效的)