企业微信服务商代开发自建应用配置以及对接
目录
1.1创建代开发应用模板
1.2模板配置开发信息
1.3模板回调处理微信后台通知事件
1.4应用回调URL配置
1.5获取代开发应用secret(permanent_code)
2.代开发应用上线
3.代开发应用获取企业微信用户信息
1.代开发应用模板创建与配置
1.1创建代开发应用模板
1.2模板配置开发信息
1.服务商代开发模板回调URL
说明:后续企业微信后台部分通知会推送到此回调URL
注意:这里配置时会要求验证代开发模板回调URL,这里可以使用官方的加解密库进行验证
服务端加解密方案说明:https://developer.work.weixin.qq.com/devtool/introduce?id=36388
官方加解密库示例下载:https://developer.work.weixin.qq.com/devtool/introduce?id=10128
- 这里以php版本为例
1.目录结构
2.验证url正确性需要参数
encodingAesKey //模板配置开发信息中的EncodingAesKey
Token //模板配置开发信息中的Token
corpId //服务商id 企业服务商后台通用开发参数里
文件:Sample.php
/*
------------使用示例一:验证回调URL---------------
*企业开启回调模式时,企业号会向验证url发送一个get请求
假设点击验证时,企业收到类似请求:
* GET /cgi-bin/wxpush?msg_signature=5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3×tamp=1409659589&nonce=263014780&echostr=P9nAzCzyDtyTWESHep1vC5X9xho%2FqYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp%2B4RPcs8TgAE7OaBO%2BFZXvnaqQ%3D%3D
* HTTP/1.1 Host: qy.weixin.qq.com
接收到该请求时,企业应
1.解析出Get请求的参数,包括消息体签名(msg_signature),时间戳(timestamp),随机数字串(nonce)以及公众平台推送过来的随机加密字符串(echostr),
这一步注意作URL解码。
2.验证消息体签名的正确性
3. 解密出echostr原文,将原文当作Get请求的response,返回给公众平台
第2,3步可以用公众平台提供的库函数VerifyURL来实现。*/
$encodingAesKey = 'In9LJMuhLVN6x5Qkg8Kx3DmHV3xdKnxlJrKJnYt8lxF';
$token = 'aMRVjNDZ';
$corpId = '';//服务商id
$sVerifyMsgSig = $_GET['msg_signature'];
$sVerifyTimeStamp = $_GET['timestamp'];
$sVerifyNonce = $_GET['nonce'];
$sVerifyEchoStr = $_GET['echostr'];
if (isset($sVerifyEchoStr)) {
//这里记录获取到的参数,以便调试
$myfile = fopen("log.txt", "w") or die("Unable to open file!");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyMsgSig: " . $sVerifyMsgSig . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyTimeStamp: " . $sVerifyTimeStamp . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyNonce: " . $sVerifyNonce . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyEchoStr: " . $sVerifyEchoStr . "\r\n");
// 需要返回的明文
$sEchoStr = "123456";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
if ($errCode == 0) {
echo $sEchoStr;
// 验证URL成功,将sEchoStr返回
// HttpUtils.SetResponce($sEchoStr);
} else {
print("ERR: " . $errCode . "\n\n");
//fwrite($myfile, "" . date('Y-m-d H:i:s') . "----errCode: " . $errCode . "\r\n");
}
fclose($myfile);
}
到这里,模板配置成功.
1.3模板回调处理微信后台通知事件
回调接口官方概述:https://developer.work.weixin.qq.com/document/path/90613
目前常用到的通知事件有
请求的包体中InfoType代表通知事件类型
1.推送suite_ticket:https://developer.work.weixin.qq.com/document/path/90628
2.授权成功通知事件:https://developer.work.weixin.qq.com/document/path/90642
这个里面拿到的auth_code可以获取到应用secret,secret用于获取应用access_token,该access_token用于获取用户敏感信息
3.重置永久授权码通知事件:https://developer.work.weixin.qq.com/document/path/94758
这个里面拿到的auth_code可以获取到应用secret,secret用于获取应用access_token,该access_token用于获取用户敏感信息
注意:
1.下面这个处理逻辑仍然是在模板回调url指向的文件里,前面url正确性验证通过后需要注释掉验证url正确性相关逻辑代码,不然会出错。
2.通知收到以后,必须立即作出响应,文档里的通知事件响应是返回一个success字符串
文件:Sample.php
1.处理事件需要参数
encodingAesKey //模板配置开发信息中的EncodingAesKey
Token //模板配置开发信息中的Token
corpId // 这里用的是刚刚创建成功的模板id
$encodingAesKey = '';
$token = '';
$corpId = ''; //模板id
$cn = new CommonFunc(); //公共函数类
$sReqMsgSig = $_GET["msg_signature"];
$sReqTimeStamp = $_GET["timestamp"];
$sReqNonce = $_GET['nonce'];
// 获取post请求的密文数据
$sReqData = file_get_contents("php://input");
$sMsg = ""; // 解析之后的明文
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $sMsg);
$myfile = fopen("log.txt", "w") or die("Unable to open file!");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----msg_signature: " . $sReqMsgSig . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----timestamp: " . $sReqTimeStamp . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----nonce: " . $sReqNonce . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sReqDatas: " . $sReqData . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sMsg: " . $sMsg . "\r\n");
if ($errCode == 0) {
// 解密成功,sMsg即为xml格式的明文
//var_dump($sMsg);
echo 'success'; //响应企业微信的请求
// TODO: 对明文的处理
/*
"<xml>
<SuiteId><![CDATA[dk623666a23e0de46f]]></SuiteId>
<AuthCode><![CDATA[uRRKjj2uhp5nLyTHb-s-32ddSru9zK-6Jy3hmuOTQFPWj-dseQ2L9bGXyfbEaWYa4jU43C5AmWl_bbGoAbfL9rKcZgqUrH_xIKtAds0Z1oc]]></AuthCode>
<InfoType><![CDATA[reset_permanent_code]]></InfoType>
<TimeStamp>1661756691</TimeStamp>
</xml>"
*/
//解析xml数据
$arr = (array)simplexml_load_string($sMsg, 'SimpleXMLElement', LIBXML_NOCDATA);
$InfoType = $arr['InfoType'];
if ($InfoType == 'reset_permanent_code') {
//重置授权码通知事件
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----AuthCode: " . $arr['AuthCode'] . "\r\n");
$auth_code_file = fopen("auth_code.txt", "w");
fwrite($auth_code_file, $arr['AuthCode']);
fclose($auth_code_file);
//保存授权应用密钥
$cn->getSecret();
} else if ($InfoType == 'suite_ticket') {
//推送suite_ticket通知事件
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----SuiteTicket: " . $arr['SuiteTicket'] . "\r\n");
$suite_ticket_file = fopen("suite_ticket.txt", "w");
fwrite($suite_ticket_file, $arr['SuiteTicket']);
fclose($suite_ticket_file);
} else if ($InfoType == 'create_auth') {
//授权成功通知通知事件
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----AuthCode: " . $arr['AuthCode'] . "\r\n");
$auth_code_file = fopen("auth_code.txt", "w");
fwrite($auth_code_file, $arr['AuthCode']);
fclose($auth_code_file);
//保存授权应用密钥
$cn->getSecret();
}
} else {
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----errCode: " . $errCode . "\r\n");
//exit(-1);
}
fclose($myfile);
公共函数类
class CommonFunc
{
/**
* 获取应用授权的token(suite_access_token)
* 获取步骤
* 1.通过suite_id(第三方应用ID),suite_secret(第三方应用密钥),suite_ticket(推送suite_ticket通知事件)换取suite_access_token
*/
public function getSuitAccessToken()
{
$suite_ticket_arr = file("suite_ticket.txt");
$suite_ticket = $suite_ticket_arr[0];
$suite_id = '';
$suite_secret = '';
//获取文件缓存中的suit_access_token
$suite_access_token_arr = file("suite_access_token.txt");
$suite_access_token = $suite_access_token_arr[0];
$last_time = strtotime($suite_access_token_arr[1]);
$now_time = time();
$save_time = $now_time + 1.5 * 60 * 60;
//一个半小时获取一次
if ($last_time - $now_time < 1800) {
$url = 'https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token';
$post_data = array(
'suite_id' => $suite_id,
'suite_secret' => $suite_secret,
'suite_ticket' => $suite_ticket
);
$header = array('Content-type' => '');
$result = $this->curl_post($url, $post_data, 5, $header, 'json');
$suite_access_token_file = fopen("suite_access_token.txt", "w");
fwrite($suite_access_token_file, $result['suite_access_token'] . "\r\n");
fwrite($suite_access_token_file, date('Y-m-d H:i:s', $save_time) . "\r\n");
fclose($suite_access_token_file);
}
return $suite_access_token;
}
/**
* 获取代开发应用secret
* 获取步骤
* 1.通过重置授权码通知事件获取auth_code(需要在服务商后台重置secret进行触发)
* 2. 获取应用授权的token(suite_access_token)
* 3.根据1,2步骤获取的参数去得到secret
*/
public function getSecret()
{
$auth_code_arr = file("auth_code.txt");
$auth_code = $this->ClearHtml($auth_code_arr[0]);
$suite_access_token = $this->ClearHtml($this->getSuitAccessToken());
$url = 'https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=' . $suite_access_token;
$post_data = array(
'auth_code' => $auth_code,
);
$header = array('Content-type' => '');
//file_get_contents进行post请求,这里使用curl,报错提示网址错误,原因未知
$res =$this->file_post($url, $post_data, 5, $header, 'json');
$result = json_decode($res,true);
$myfile = fopen("log.txt", "w");
fwrite($myfile, $res);
fclose($myfile);
$secret = $result['permanent_code'];
$secret_file = fopen("secret.txt", "w");
fwrite($secret_file, $secret);
fclose($secret_file);
}
/**
*
* curl post请求
*
*/
public function curl_post($url, $post_data = array(), $timeout = 5, $header = "", $data_type = "")
{
$header = empty($header) ? '' : $header;
//支持json数据数据提交
if ($data_type == 'json') {
$post_string = json_encode($post_data);
} elseif ($data_type == 'array') {
$post_string = $post_data;
} elseif (is_array($post_data)) {
$post_string = http_build_query($post_data, '', '&');
}
$ch = curl_init(); // 启动一个CURL会话
curl_setopt($ch, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查 // https请求 不验证证书和hosts
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
curl_setopt($ch, CURLOPT_POST, true); // 发送一个常规的Post请求
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); // Post提交的数据包
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); // 设置超时限制防止死循环
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 获取的信息以文件流的形式返回
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //模拟的header头
$result = curl_exec($ch);
if (curl_errno($ch)) {
$curl_log = fopen("curl_log.txt", "w");
fwrite($curl_log, date('Y-m-d H:i:s') . '--curl请求错误网址:' . $url . "\r\n");
fwrite($curl_log, date('Y-m-d H:i:s') . '--curl请求错误' . curl_errno($ch) . "\r\n");
fclose($curl_log);
}
$result = json_decode($result, true);
curl_close($ch);
return $result;
}
/**
* file_get_contents进行post请求
*/
public function file_post($url, $post_data, $timeout = 5, $header = "", $data_type = "")
{
$postdata = json_encode($post_data);
$options = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type:application/x-www-form-urlencoded',
'content' => $postdata,
'timeout' => 15 * 60 // 超时时间(单位:s)
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
return $result;
}
public function ClearHtml($str)
{
$str = trim($str); //清除字符串两边的空格
$str = strip_tags($str, ""); //利用php自带的函数清除html格式
$str = preg_replace("/\t/", "", $str); //使用正则表达式替换内容,如:空格,换行,并将替换为空。
$str = preg_replace("/\r\n/", "", $str);
$str = preg_replace("/\r/", "", $str);
$str = preg_replace("/\n/", "", $str);
$str = preg_replace("/ /", "", $str);
$str = preg_replace("/ /", "", $str); //匹配html中的空格
return trim($str); //返回字符串
}
}
到这里模板回调url配置完成。
1.4应用回调URL配置
这里暂时用已配置好的代开发应用配置示例
注意:这里Token和EncodingAESKey最好和模板配置开发时的保持一致
文件:CorpSample.php
验证URL回调
注意,应用的验证URL回调与模板的验证URL回调有点不一样这里不是直接用服务商id
这里企业主体的corpid需要转换为服务商的密文corpid
corpid转换: 代开发应用安全性升级 - 接口文档 - 企业微信开发者中心
获取服务商凭证:https://developer.work.weixin.qq.com/document/path/91200
所需要的参数
$encodingAesKey = ''; //配置的encodingAesKey
$token = '';//配置的token
$corpId = ''; //企业号id 登录企业微信后台,我的企业即可看到
//服务商通用开发参数
$sCorpid = '';
$sProviderSecret = '';
逻辑代码
$encodingAesKey = 'In9LJMuhLVN6x5Qkg8Kx3DmHV3xdKnxlJrKJnYt8lxF';
$token = 'aMRVjNDZ';
$corpId = ''; //企业号id
//服务商通用开发参数
$sCorpid = '';
$sProviderSecret = '';
$cn = new CommonFunc();
//获取服务商凭证
$sProviderToken = $cn->getProviderToken($sCorpid, $sProviderSecret);
$provider_access_token = $sProviderToken['provider_access_token'];
//获取转化呢后的密文corpid
$open_corpid_data = $cn->corpidChange($corpId, $provider_access_token);
$open_corpid = $open_corpid_data['open_corpid'];
$sVerifyMsgSig = $_GET['msg_signature'];
$sVerifyTimeStamp = $_GET['timestamp'];
$sVerifyNonce = $_GET['nonce'];
$sVerifyEchoStr = $_GET['echostr'];
$myfile = fopen("log.txt", "w") or die("Unable to open file!");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyMsgSig: " . $sVerifyMsgSig . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyTimeStamp: " . $sVerifyTimeStamp . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyNonce: " . $sVerifyNonce . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----sVerifyEchoStr: " . $sVerifyEchoStr . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----provider_access_token: " . $provider_access_token . "\r\n");
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----open_corpid: " . $open_corpid . "\r\n");
// 需要返回的明文
$sEchoStr = "123456";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $open_corpid);
$errCode = $wxcpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
if ($errCode == 0) {
echo $sEchoStr;
// 验证URL成功,将sEchoStr返回
// HttpUtils.SetResponce($sEchoStr);
} else {
fwrite($myfile, "" . date('Y-m-d H:i:s') . "----errCode: " . $errCode . "\r\n");
}
fclose($myfile);
class CommonFunc
{
/**
* 获取服务商的token
* @param string $sCorpId 服务商管理后台通用开发参数-corpid
* @param string $sProviderSecret 服务商管理后台通用开发参数-ProviderSecret
*/
public function getProviderToken($sCorpid, $sProviderSecret)
{
$url = 'https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token';
$res = $this->curl_post($url, array('corpid' => $sCorpid, 'provider_secret' => $sProviderSecret), 5, array('Content-type' => ''), 'json');
return $res;
}
/**
*
* corpid转化
* @param string $sCorpid 企业明文corpid
* @param string $sToken 服务商token
*/
public function corpidChange($sCorpId, $sToken)
{
$url = 'https://qyapi.weixin.qq.com/cgi-bin/service/corpid_to_opencorpid?provider_access_token=' . $sToken;
$res = $this->curl_post($url, array('corpid' => $sCorpId), 5, array('Content-type' => ''), 'json');
return $res;
}
/**
*
* curl post请求
*
*/
public function curl_post($url, $post_data = array(), $timeout = 5, $header = "", $data_type = "")
{
$header = empty($header) ? '' : $header;
//支持json数据数据提交
if ($data_type == 'json') {
$post_string = json_encode($post_data);
} elseif ($data_type == 'array') {
$post_string = $post_data;
} elseif (is_array($post_data)) {
$post_string = http_build_query($post_data, '', '&');
}
$ch = curl_init();//启动一个CURL会话
curl_setopt($ch, CURLOPT_URL, $url); // 要访问的地址
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //对认证证书来源的检查https请求 不验证证书和hosts
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//从证书中检查SSL加密算法是否存在
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);//模拟用户使用的浏览器
curl_setopt($ch, CURLOPT_POST, true); // 发送一个常规的Post请求
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); // Post提交的数据包
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);// 设置超时限制防止死循环
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 获取的信息以文件流的形式返回
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //模拟的header头
$result = curl_exec($ch);
$result = json_decode($result, true);
curl_close($ch);
return $result;
}
至此,应用回调url配置成功
1.5获取代开发应用secret(permanent_code)
1.通过suite_id(模板ID),suite_secret(模板Secret),suite_ticket(推送suite_ticket通知事件)换取suite_access_token
参考:https://developer.work.weixin.qq.com/document/path/90600
2.通过前面的授权成功通知事件或者重置永久授权码通知事件拿到的auth_code,
3.根据1,2步骤获取的参数去得到secret(获取企业永久授权码)
参考:https://developer.work.weixin.qq.com/document/path/90603
2.代开发应用上线
3.代开发应用获取企业微信用户信息
注意:这里需要授权企业登录企业微信后台对用户信息进行授权
这部分参考的是企业微信的自建应用开发,不是第三方应用开发
需要参数
'permanent_code'=>'',//代开发应用secret(永久授权码)
'corpid'=>'',//企业明文CorpID
'redirect_url'=>'',//授权重定向地址
'agentid'=>'1000006',//应用id
1.通过OAuth2的授权登录,前端获取到code,
- 参考:https://developer.work.weixin.qq.com/document/path/91119
- 2.后端通过前端提交的code,去获取access_token
- 这里面的corpsecret是前面获取的企业永久授权码 corpid就是企业明文id
- 参考:https://developer.work.weixin.qq.com/document/path/91039
- 3.获取访问用户身份
- 这里会获取到用户的USERID,scope为snsapi_privateinfo,且用户在应用可见范围之内时返回user_ticket,
- 后续利用该参数可以获取用户信息或敏感信息
- 参考:https://developer.work.weixin.qq.com/document/path/91023
- 4.获取用户详情
- 这里获取的是用户的基本信息,不包括敏感信息
- 参考:https://developer.work.weixin.qq.com/document/path/90196
- 5.获取用户敏感信息
- 参考https://developer.work.weixin.qq.com/document/path/95833
这里有个坑:使用开发者工具调试时,只能获取一次敏感信息,后续就不能,后来研究发现应该是微信开发者工具还未优化好这一部分,在真机上测试是正常的。开发者工具版本:1.06.2208010
weixin_71077816: 我的安装的时候没显示jre正常吗
挥洒寂寞: 请教一个问题,同一个模板如何给多个企业使用
CSDN-Ada助手: 哇, 你的文章质量真不错,值得学习!不过这么高质量的文章, 还值得进一步提升, 以下的改进点你可以参考下: (1)增加内容的多样性(例如使用标准目录、标题、图片、链接、表格等元素);(2)使用更多的站内链接;(3)使用标准目录。
ctotalk: 努力奋斗