最近公司想开发企业微信端的Worktile。之前是企业微信的内部应用,所以只适用于私有部署客户,公有云客户无法使用。这篇文章都是准备开发企业微信的第三方应用,主要介绍一下在调研阶段遇到的小技巧。开发前需要注册第三方服务商,然后使用第三方服务商的账号创建应用。创建后,只需要管理员对应用进行授权,第三方服务商即可为用户提供服务。1.注册第三方服务商登录服务商官网,注册服务商,登录服务商管理后台。2.配置开发信息在创建应用之前,首先要配置开发的通用参数。图片说明填写系统事件接收url时,必须正确响应企业微信验证url请求。这个可以参考企业微信后台和自建应用接收消息的API设置。在企业管理端后台,进入需要设置接收消息的目标应用,点击“接收消息”的“设置API接收”按钮,进入配置页面。需要填写应用URL、Token、Encoding三个参数AESKeyURL是企业后台接收企业微信推送请求的访问协议和地址,支持http或https协议(为了提高安全性,建议使用https)。Token可以由企业任意填写,用于生成签名。EncodingAESKey用于加密消息体,是AES密钥的Base64编码。2.1验证url的合法性点击保存后,会向企业微信填写的url发送get请求。例如url设置为https://api.worktile.com,企业微信会发送如下验证请求:请求地址:https://api.worktile.com/?msg...×tamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR参数说明msg_signature企业微信加密签名,msg_signature结合了企业填写的token,请求中的时间戳,nonce参数,加密消息体时间戳timestampnonce随机数echostr加密字符串。需要解密才能得到消息内容的明文。解密后有四个字段:random、msg_len、msg、receiveid,其中msg为消息内容的明文。2.1.1通过参数msg_signature验证请求。timestamp,nonce,msg_encrypt进行sha1加密,这里我们可以直接使用npm模块sha1进行加密,然后判断得到的str是否等于msg_signature。函数sha1(str){constmd5sum=crypto.createHash('sha1');md5sum.update(str);constciphertext=md5sum.digest('hex');返回密文;}functioncheckSignature(req,res,encrypt){constquery=req.query;console.log('请求地址:',req.url);constsignature=query.msg_signature;consttimestamp=query.timestamp;constnonce=query.nonce;让回声;console.log('加密',加密);如果(!encrypt){echostr=query.echostr;}else{echostr=加密;}console.log('时间戳:',时间戳);console.log('nonce:',nonce);console.log('签名:',签名);//按字典顺序对token/timestamp/nonce进行排序consttmpArr=[token,timestamp,nonce,echostr];consttmpStr=sha1(tmpArr.sort().join(''));console.log('Sha1String:',tmpStr);//验证排序和加密后的字符串是否等于签名if(tmpStr===signature){//原样返回echostr参数内容constresult=_decode(echostr);console.log('last',结果);console.log('检查成功');返回结果;}else{console.log('检查失败');返回“失败”;}}2.1.2解密echostr得到msg返回密文解密过程:base64decodeconstEncodingAESKey为刚刚生成的AESKey='21IpFqj8qolJbaqPqe1rVTAK5sgkaQ3GQmUKiUQLwRe';letaesKey=Buffer.from(EncodingAESKey+'=','base64');对AESKey进行aes-256-cbc解密函数_decode(data){letaesKey=Buffer.from('21IpFqj8qolJbaqPqe1rVTAK5sgkaQ3GQmUKiUQLwRe'+'=','base64');让aesCipher=crypto.createDecipheriv("aes-256-cbc",aesKey,aesKey.slice(0,16));aesCipher.setAutoPadding(false);让decipheredBuff=Buffer.concat([aesCipher.update(data,'base64'),aesCipher.final()]);decipheredBuff=PKCS7Decoder(decipheredBuff);让len_netOrder_corpid=decipheredBuff.slice(16);让msg_len=len_netOrder_corpid.slice(0,4).readUInt32BE(0);constresult=len_netOrder_corpid.slice(4,msg_len+4).toString();返回结果;//返回解密后的明文-}functionPKCS7Decoder(buff){varpad=buff[buff.length-1];如果(垫<1||垫>32){垫=0;}returnbuff.slice(0,buff.length-pad);}然后将结果返回给res.end(result);2.2回调url验证失败问题验证在使用url的时候,经常会遇到url验证失败的问题。解决方法是使用微信企业号接口调试工具。授权时,企业微信向刚才应用设置的命令回调url发送post请求,例如:https://api.worktile.com/worktile?msg_signature=b99605616153ffbfbe6ebbb500bd211e67ed714d×tamp=1551076894&nonce=1551709703,直接返回回调每个事件成功时。服务提供者必须在收到推送后直接返回字符串“success”。如果返回值不是“success”,企业微信会将返回内容视为错误信息。app.post('/worktile',function(req,res){console.log('req.body',req.body);res.send('success');});测试应用安装须知测试用企业微信需要服务商注册,每个应用支持同时添加10个测试用企业微信。安装测试企业微信号使用当前应用配置信息,后续修改不会同步;如需更新应用信息,请重新授权安装同一个企业微信公众号。不支持同时安装测试应用和正式发布的应用。6.用户网页授权登录6.1构建第三方应用网页授权链接如果第三方应用需要在打开的网页中携带用户身份信息,首先构建如下链接获取代码:https://open.weixin.qq.com/co...参数必须指明appid为第三方应用id(即以ww或wx开头的suite_id)。注意redirect_uri是授权后重定向的回调链接地址,请使用urlencode处理链接。注意域名需要设置为第三方应用的可信域名。response_type为返回类型,此时固定:codescope为应用授权范围。snsapi_base:静默授权,可获取会员基本信息(UserId和DeviceId);snsapi_userinfo:静默授权,可获取会员详细信息,但不包括手机、邮箱等敏感信息;snsapi_privateinfo:手动授权,可获取会员详细信息,包括手机、邮箱等敏感信息。state否重定向后会带上state参数。企业可填写a-zA-Z0-9参数值,长度不能超过128字节。#wechat_redirect是终端使用该参数判断企业员工点击后是否需要带身份信息,页面会跳转到redirect_uri?code=CODE&state=STATE,第三方应用可以获得corpid和userid公司员工按代码参数。最大代码长度为512字节。6.2获取接入用户身份请求方式:GET(HTTPS)请求地址:https://qyapi.weixin.qq.com/c...参数必须注明access_token为第三方应用的suite_access_token,见“获取第三方应用凭证”代码为通过会员授权获得的代码,最大长度为512字节。每个会员授权所带的code会不同,code只能使用一次,5分钟不使用会自动失效。6.2.1获取第三方应用的suite_access_token请求方式:POST(HTTPS)请求地址:https://qyapi.weixin.qq.com/c...参数是否必须注明suite_id为应用idww或wx开头(对应Fortheoldsuiteidstartingwithtj)suite_secret为应用secretsuite_ticket为企业微信后台推送的ticket。由于第三方服务商可能托管大量企业,安全问题的影响会更加严重。因此,API中除了对合法来源IP进行验证之外,还额外增加了一个suite_ticket作为安全凭证。获取suite_access_token时,需要suite_ticket参数。suite_ticket由企业微信后台定时推送到“命令回调地址”,每十分钟更新一次,详见pushsuite_ticket。suite_ticket实际有效期为30分钟,可以容错连续两次获取suite_ticket失败,但请务必使用最新收到的suite_ticket。通过该接口获取的suite_access_token,有效期为2小时。开发者需要缓存,不能频繁获取。6.2.2获取pushsuite_ticket企业微信服务器会定时(每十分钟)推送工单。工单会实时变化,用于后续接口调用。请求方式:POST(HTTPS)请求地址:https://api.ninesix.cc/workti...当授权、通讯录变更、工单变更等事件发生时,企业微信服务器会发送“命令application的callbackURL”推送相应的事件消息,nodejs收到xml,解析得到encrypt字段,然后使用上面配置通用开发参数url时使用的解密方式,得到suite_ticket。6.3获取用户敏感信息的请求方式:POST(HTTPS)请求地址:https://qyapi.weixin.qq.com/c...{"user_ticket":"USER_TICKET"}参数必须注明access_token为第三方应用的suite_access_token,参见“获取第三方应用凭证”user_ticket为会员票返回结果:{"errcode":0,"errmsg":"ok","corpid":"wwxxxxxxyyyyy","userid":"lisi","name":"李斯","mobile":"15913215421","gender":"1","email":"xxx@xx.com","avatar":"http://shp.qpic.cn/bizmp/xxxxxxxxxxx/0","qr_code":"https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=vcfc13b01dfs78e981c"}7.用户授权成功首页详情page8.我们可以向用户发送消息推送文字、图片、视频、文件、图文等类型。请求方式:POST(HTTPS)请求地址:https://qyapi.weixin.qq.com/c...推送时需要access_token和申请agentId。第三方服务商可以通过获取这个Parameter值的接口获取企业授权信息,其实直接获取企业永久授权码就可以获取这两个值。我们测试安装应用成功后,企业微信会向指令回调地址发起请求。通过上面的解密方法,可以解析到xml中的auth_code,然后通过https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=SUITE_ACCESS_TOKEN和auth_code可以得到access_token和agentId,以及返回的代理是一个数组,但是旧的多应用授权时只会返回多个代理,新的单应用授权总是只返回一个代理。然后通过access_token和agentId,就可以愉快的给用户发消息了。当你点击链接时,可以跳转到指定的任务或日程等,但返回时,还是在企业微信的消息模块中,无法自动打开第三方应用。客服回复不支持这个。9.注意事项该api可能具有时效性。如有差异,以官方api为准。完整demoWorktile官网:worktile.com本文作者:王鹏本文首发于“Worktile官博”,转载请注明出处。
