企业微信获取第三方应用凭证
上一篇介绍了如何配置通用开发参数及通过url回调验证,
本篇将通过服务商后台配置关联小程序应用配置和获取第三方凭证及如何配置企业可信IP。
当然上篇配置的回调设置也不会白费,在下方的指令和数据回调会用到。
第三方应用开发流程
官方企业微信第三方应用开发的基本流程如下:

小程序应用设置
获取suite_ticket需要使用SuiteID参数,因为项目使用的是三方应用方式开发,
这里可以通过设置小程序->关联小程序详情。
前面的配置基础信息与应用权限不在介绍,比较简单。

配置内容描述
应用主页:必填,用户从企业微信工作台进入应用时将直接跳转到对应主页URL
桌面端独立主页:用户从企业微信桌面端工作台进入应用时跳转的应用页面URL
可信域名:仅支持可信域名内的应用调用OAuth2授权、JSSDK等
安装完成回调域名:用户安装成功后可指定跳转至该域名的链接
业务设置URL:授权企业的管理员可从企业微信后台的应用详情页免登录直接跳转该链接进行应用配置。
数据回调URL:必填,用于正确响应企业微信验证URL的请求,用于接收托管企业微信应用的用户信息、进入应用事件、通讯录变更事件。
指令回调URL:必填,用户正确响应企业微信验证URL的请求,用于接收应用(添加、删除、修改)以及ticket参数等API请求。
Secret是可以生成和改变的,SuiteID是系统生成的。

配置可信域名
配置可信域名:先填写网站域名,不要写http或者https前缀,直接是网站域名。
如下:

具体怎么验证可信域名呢,接着点击检验可信域名归属:

需要把这个文件下载后,上传到网站域名下,并且可以访问到;
放好之后,如果验证不成功,可能有缓存,等待几分钟在尝试。
设置回调配置
这几项就关于回调的参数, 之前配置过url/token/encodingAESKey直接再粘贴过来即可。
指令回调和数据回调地址可以写上篇完成的回调地址,只是要注意现在的token和EncodingAESKey换成之前调好的token和EncodingAESKey和字符串就行或者也可以把代码中的这两项换成现在新生成的。

这些可以分步配置,最后保存完成后,效果如下图:

如果暂时不知道设置项是什么作用,可以先配置一个,之后用上了再改即可。
获取ticket
设置好应用参数后,把suiteID、secret粘贴一下,在代码中要使用。
控制器接收参数
这里因为一开始不确定参数情况,所以对接收的所有参数存储到日志中。
首先判断请求方式为post,然后具体进入接收数据环节。
经过多次调试后确定需要接收四个参数内容,其中msg_signature/timestamp/nonce三个可直接通过字段名获取到,比较麻烦的是密文数据,需要使用php://input获取。
内容是一个xml格式的字符串。
内容如下:
public function wxNotify()
{$obj = new CompanyWxPushService();$all = request()->all();writeRecordLog('companyWechat.log', request()->method());writeRecordLog('companyWechat.log', var_export($all, true));if (request()->isMethod('POST')) {$msg_signature = request()->input('msg_signature');$timeStamp = request()->input('timestamp');$nonce = request()->input('nonce');// post请求的密文数据$sReqData = file_get_contents('php://input');echo $obj->callbackPOST($msg_signature, $timeStamp, $nonce, $sReqData);} die();
}
业务层解析
下面的处理中思路是:收到post请求之后
1.解析出url上的参数,包括消息体签名(msg_signature),时间戳(timestamp)以及随机数字串(nonce);
2.验证消息体签名的正确性。
3.将post请求的数据进行xml解析,并将<Encrypt>标签的内容进行解密,
解密出来的明文是xml格式的字符串需要转变为xml对象。
4.判断对象的infotype获取suite_ticket并进行存储,之后调用access_token需要使用。
内容如下:
class CompanyWxPushService
{// 接收信息时的加解密参数protected static $encodingAesKey = "随机生成的encodingAESKey";// 接收信息时的校验Tokenprotected static $token = "随机生成的token";protected static $corpId = "服务商注册后获取的corpID";protected static $SuiteID = '小程序应用详情获取的suiteID';protected static $Secret = '小程序应用详情获取的secret';/*** 企业微信post回调解析* @param $sReqMsgSig* @param $sReqTimeStamp* @param $sReqNonce* @param $sReqData* @return string*/public function callbackPOST($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData){$sMsg = ""; // 解析之后的明文$wxcpt = new \WXBizMsgCrypt(self::$token, self::$encodingAesKey, self::$SuiteID);$errCode = $wxcpt->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $sMsg);writeRecordLog('companyWechat.log', $errCode);if ($errCode == 0) {// 解密成功,sMsg即为xml格式的明文writeRecordLog('companyWechat.log', "解密成功:\r\n". var_export($sMsg, true));// TODO: 对明文的处理// 解析该xml字符串,利用simpleXMLlibxml_disable_entity_loader(true);//禁止xml实体解析,防止xml注入$xml = simplexml_load_string($sMsg, 'SimpleXMLElement', LIBXML_NOCDATA);switch ($xml->InfoType) {case 'suite_ticket': // 存储suite_ticket$log_desc = '存储suite_ticket:' . $xml->SuiteTicket;self::$redisWechat->setCompanyTicket($xml->SuiteTicket);break;default:$log_desc = '未知的类型:' . $xml->InfoType;break;}writeRecordLog('companyWechat.log', '解析日志:' . $log_desc);return 'success';} else {return "ERR: " . $errCode . "\n\n";}}
}
获取ticket
企业微信服务器会定时(每十分钟)向指令回调 URL 推送 suite_ticket,在指令回调的后台逻辑中解密消息体即可得到对应的 InfoType 和 SuiteTicket 。
不同类型的指令回调会通过不同的 InfoType 进行区分,在每次解密得到 suite_ticket 后,应在服务端临时缓存起来。
也可通过刷新ticket按钮来多次快速获取验证解析。

日志情况如下:

获取 suite_access_token
获取suite_access_token即为获取第三方凭证,这个在整个开发中很重要,接下来的几个接口都需要三方凭证;在成功接受并且缓存 suite_ticket 之后,我们可以主动来获取 suite_access_token。获取suite_access_token时,需要 suite_id,suite_secret suite_ticket 作为参数。
请求方式
POST
请求地址
https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token
参数说明
| 参数 | 是否必须 | 说明 |
| suite_id | 是 | 第三方应用id或者代开发应用模板id。第三方应用以ww或wx开头应用id(对应于旧的以tj开头的套件id);代开发应用以dk开头 |
| suite_secret | 是 | 第三方应用secret 或者代开发应用模板secret |
| suite_ticket | 是 | 企业微信后台推送的ticket,即回调中获取的ticket |
控制器
暂时先写一个请求,看一下返回内容。
/*** 获取企业微信 suite_access_token*/
public function companyAccessToken()
{$obj = new CompanyWxPushService();print_r($obj->companyAccessToken());die;
}
业务层处理
需要三方应用SuiteID和Secret参数和之前回调返回中解密后获取的suite_ticket。
Suite_ticket这一步是通过回调获取的suite_ticket内容,设置的redis缓存。
/*** 获取企业微信 suite_access_token* @return bool|string*/
public function companyAccessToken()
{$url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token";$params = ['suite_id' => self::$SuiteID,'suite_secret' => self::$Secret,'suite_ticket' => self::$redisWechat->getCompanyTicket()];return $this->linkCurl($url, 'POST', $params);
}/*** 请求接口返回内容* @param $url : 请求的URL地址* @param $method : 请求方式POST|GET* @param bool $params : 请求的参数* @return bool|string*/
protected function linkCurl($url, $method, $params = false)
{$ch = curl_init();curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_FAILONERROR, false);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);if (strpos("$" . $url, "https://") == 1) {curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);}curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);curl_setopt($ch, CURLOPT_TIMEOUT, 60);if ($method == "POST") {curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_POSTFIELDS, json($params));} else if ($params) {curl_setopt($ch, CURLOPT_URL, $url . '?' . http_build_query($params));}$response = curl_exec($ch);if ($response === FALSE) return false;curl_close($ch);return $response;
}
响应:
{"errcode":60020,"errmsg":"not allow to access from your ip:client ip 123......."}
怀疑是没注册应用和配置企业可信ip。
响应60020是未设置IP白名单,可查看下方第三方应用企业可信IP设置。
设置后,再次请求返回正常。
{"suite_access_token":"tpBubbmIlo_N5zoCKXvz_9q6IanamtrZMB2rBI_Mnh48m9Fqwr7dwak3QZZPJA5O7KBQ_736ks3yogBT5djZerXrajnDUk9hsvP2xGVMnJSq1jPKDvlzIbayUoIX3lCr","expires_in":7200}
响应参数描述
| 参数 | 是否必须 | 说明 |
| suite_id | 是 | 第三方应用id或者代开发应用模板id。第三方应用以ww或wx开头应用id(对应于旧的以tj开头的套件id);代开发应用以dk开头 |
| suite_secret | 是 | 第三方应用secret 或者代开发应用模板secret |
| suite_ticket | 是 | 企业微信后台推送的ticket |
企业可信IP设置
第三方应用或待开发应用需要在服务商后台->服务商信息->基本信息
中设置ip白名单;1分钟后生效。

设置白名单

注意:一般员工账号看不到,需要切换管理员账号。
总结
通过第三方应用开发流程可以知道获取第三方凭证suite_access_token的原因,在之后的API中是一个很重要的参数;在获取的途中,因为不熟悉遇到设置白名单的问题,权限是普通权限看不到服务商功能,之后才发现是权限问题,希望其他开发者不会有这个困惑。
相关文章:
企业微信获取第三方应用凭证
上一篇介绍了如何配置通用开发参数及通过url回调验证, 本篇将通过服务商后台配置关联小程序应用配置和获取第三方凭证及如何配置企业可信IP。 当然上篇配置的回调设置也不会白费,在下方的指令和数据回调会用到。 第三方应用开发流程 官方企业微信第三方…...
增删改查mysql
查询 -- 查询表结果-- 查看 当前数据库下的表show tables;-- 查看指定的表desc tb_emp; -- td_emp 是表名-- 查看 数据库的见表语句show create table tb_emp; 修改 -- 修改表结构 -- 修改 为表 tb_emp 添加字段 qq varchar(11) alter table tb_emp add qq varchar(11) …...
【vue】下载导出excel
下载导出excel 首先使用的tdesign框架,要导出后端返回的数据流excel 遇见的问题 下载的文件,里边的内容是undefined 观察报错 一看就知道并不是后端的报错,后端不可能是undefined 在强烈的好奇心驱动下,看了下接口࿰…...
c#正则表达式
using System.Text.RegularExpressions; namespace demo1 {/// <summary>/// 正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a~z的字母)和特殊字符(称为“…...
C#密封类和密封成员
密封类和密封成员需要使用 sealed 修饰符,他可以防止当前类被继承或者防止派生类在继承的过程中重写某个方法。 与abstract抽象修饰符类似,sealed 修饰符不仅可用来修饰class,同样也可以修饰类成员。如果sealed关键词用在class上,…...
三、Eureka注册中心
目录 一、作用及调用方式 二、搭建eureka注册中心 三、注册user-service和order-service 四、新增实例 五、服务拉取 六、总结 一、作用及调用方式 在服务提供者启动时,它会向eureka注册中心提供自己的信息,并每30秒进行一次刷新eureka注册中心保存…...
java线程池动态调节功能实现
java线程池动态调节功能实现 功能背景ThreadPoolExecutor配置自定义可变容LinkedBlockingQueuecontroller接口测试结果 功能背景 由于线程池的参数配置是一个比较难准确配置好, 如果需要进行配置修改, 就会对配置进行修改,再进行部署,影响效率, 或者应用场景的变化,导致固定的…...
KT148A语音芯片使用串口uart本控制的完整说明_包含硬件和指令举例
一、功能简介 KT148A肯定是支持串口的,有客户反馈使用一线还是不方便,比如一些大型的系统不适合有延时的操作,所以更加倾向于使用uart控制,这里我们也给出解决方案 延伸出来另外一个版本,KT158A 注意次版本芯片还是…...
kubectl 本地远程链接k8s多个集群,远程管控多集群,查看日志 部署服务(windows版)
文章目录 一、前言二、windows上安装kubectl和mobaxterm2.1 准备安装包2.2 安装kubectl2.3 链接k8s集群2.4 查看某一个pod的容器日志2.5 切换context 上下文配置,实现在多个k8s集群间动态切换 一、前言 现如今是一个万物皆上云 的时代,各种云层出不穷&am…...
wireshark打开tcpdump抓的包 vwr: Invalid data length runs past the end of the record
tcpdump -i any -n -s0 > t.pcap 使用此命令在Debian系统上抓包,下载到PC,用wireshark打开时报错: 后来发现写入文件时使用 -w 是没问题的,原因还不清楚。 tcpdump -i any -n -s0 -w t.pcap...
Python爬虫教程:从入门到实战
更多Python学习内容:ipengtao.com 大家好,我是涛哥,今天为大家分享 Python爬虫教程:从入门到实战,文章3800字,阅读大约15分钟,大家enjoy~~ 网络上的信息浩如烟海,而爬虫(…...
C++实现高频设计模式
面试能说出这几种常用的设计模式即可 1.策略模式 1.1 业务场景 大数据系统把文件推送过来,根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码: if(type"A"){//按照A格式解析 }else if(type"B"){//按照B格式解析 …...
opencv(2): 视频采集和录制
视频采集 相关API VideoCapture()cap.read(): 返回两个值,第一个参数,如果读到frame,返回 True. 第二个参数为相应的图像帧。cap.release() VideoCapture cv2.VideoCapture(0) 0 表示自动检测,如果在笔记本上运行&…...
SpringBoot+EasyExcel设置excel样式
方式一:使用注解方式设置样式 模板可通过HeadFontStyle、HeadStyle、ContentFontStyle、ContentStyle、HeadRowHeight ContentRowHeight等注解设置excel单元格样式; //字体样式及字体大小 HeadFontStyle(fontName "宋体",fontHeightInPoints…...
自定义View之Measure(二)
measure 用来测量 View 的宽和高,它的流程分为 View 的 measure 流程和 ViewGroup 的measure流程,只不过ViewGroup的measure流程除了要完成自己的测量,还要遍历地调用子元素的measure()方法。 上一回说到performMeasur…...
SQL注入学习--GTFHub(布尔盲注+时间盲注+MySQL结构)
目录 布尔盲注 手工注入 笔记 Boolean注入 # 使用脚本注入 sqlmap注入 使用Burpsuite进行半自动注入 时间盲注 手工注入 使用脚本注入 sqlmap注入 使用Burpsuite进行半自动注入 MySQL结构 手工注入 sqlmap注入 笔记 union 联合注入,手工注入的一般步骤 …...
Kubernetes学习-概念2
参考:关于 cgroup v2 | Kubernetes 关于 cgroup v2 在 Linux 上,控制组约束分配给进程的资源。 kubelet 和底层容器运行时都需要对接 cgroup 来强制执行为 Pod 和容器管理资源, 这包括为容器化工作负载配置 CPU/内存请求和限制。 Linux 中…...
StyleGAN:彻底改变生成对抗网络的艺术
一、介绍 多年来,人工智能领域取得了显着的进步,其中最令人兴奋的领域之一是生成模型的发展。这些模型旨在生成与人类创作没有区别的内容,例如图像和文本。其中,StyleGAN(即风格生成对抗网络)因其创建高度逼…...
黑马程序员微服务第四天课程 分布式搜索引擎1
分布式搜索引擎01 – elasticsearch基础 0.学习目标 1.初识elasticsearch 1.1.了解ES 1.1.1.elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 例如: …...
向量以及矩阵
0.前言 好了那我们新的征程也即将开始,那么在此呢我也先啰嗦两句,本篇文章介绍数学基础的部分,因为个人精力有限我不可能没一字一句都讲得非常清楚明白,像矩阵乘法之类的一些基础知识我都是默认你会了(还不会的同学推…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
