钉钉配置事件订阅(Python)
钉钉配置事件订阅
0.需求分析
需要实现钉钉企业通讯录同步至企业微信通讯录,这就需要用到钉钉的事件与回调
1.配置应用
登陆开放平台
https://open-dev.dingtalk.com/
去企业内部开发里面,先创建个应用,后面都借用这个应用来调接口
创建完成应用后进入应用,找到下面红框内的数据,后面会用到
进入应用中的事件与回调,自动生成aes_key
和token
,然后保存就好这两个数据
2.服务开发
2.1请求验证
配置上面1.配置应用
中的事件订阅下的请求网址如http://yourserver/api/callback
时,需要把这个网址的接口开发好。钉钉会发送一个类似下面这样的请求:
一个POST类型的http
请求,携带了部分url
参数和json
的加密参数
请求的路径参数包括signature
、msg_signature
、timestamp
、nonce
,请求体只有一个encrypt
的json
curl -X 'POST' \'http://yourserver:80/api/callback?signature=369beedea8d1c8d1ad18936e827d29d0c8415baf&msg_signature=369beedea8d1c8d1ad18936e827d29d0c8415baf×tamp=1660634610203&nonce=kBms4hUF' \-H 'accept: application/json' \-H 'Content-Type: application/json' \-d '{"encrypt": "4Q4JHq88OCR3P+8v2mcFHLT6dmaaYAckaUBVk1spJnCx7u9raGZVAxVUIuQg3loL8LjIQj+5YC3+HJcehTsJXu1qMOv5TKdb4+koO55g8WCYZP/vebg2RZQC2gBlN2zv"
}'
钉钉服务器会向配置好的接口服务中发送请求,用来验证双方通信的真实性,接口服务端可通过解密encrypt中的数据来验证是否是来自钉钉服务器,而返回的加密的success字符串能让钉钉服务器验证是否是来自用户,属于双向验证
2.2接口开发
2.2.1技术选型
对于创建接口服务这种需求,钉钉开放官网文档有些示例,比如Java等,都是可以实现的,因为需求很简单,故采用胶水语言Python进行开发
Python中想做接口服务,有两种选择,一是Flask轻量化接口服务,二是Django(Python Web框架)。经过简单比较后,选择Flask轻量化接口服务
主要代码如下:
DingDingCallback
# !/usr/bin/python3
# encoding:utf-8
'''
dingding通讯录同步至企业微信
采用Flask写一个实时监听的接口
收到钉钉的通讯录变更请求后,修改请求中的数据直接请求企业微信通讯录相关的API
'''
import flask
import json
import DingTalkCrypto# 钉钉事件订阅aeskey
aes_key = "xxxxxxxxxxxxxT329ssbVn5Bo"
# 钉钉事件订阅token
token = "xxxxxxxxxxxIgBpKsBE"
# 钉钉appkey
app_key = "xxxxxxxxxxxxxxx"# 实例化api,把当前这个python文件当作一个服务,__name__代表当前这个python文件
api = flask.Flask(__name__)# 'index'是接口路径,methods不写,默认get请求
@api.route('/sync/test', methods=['get'])
# get方式访问
def index():ren = {'msg': '成功访问首页', 'msg_code': 200}print("测试接口成功请求!!!")# json.dumps 序列化时对中文默认使用的ascii编码.想输出中文需要指定ensure_ascii=Falsereturn json.dumps(ren, ensure_ascii=False)# post方式访问(josn格式参数)
@api.route('/sync', methods=['post'])
def loginjosn():# 1.通过flask获取请求中的参数列表args = flask.request.args# 2.获取需要解密的参数signature = args.get('signature') # 实际打印中signature和msg_signature是一样的msg_signature = args.get('msg_signature')timestamp = args.get('timestamp')nonce = args.get('nonce')# 3.获取post请求中的json数据encrypt = flask.request.json.get('encrypt')print(encrypt)# 4.调用加密解密工具类# DingCallbackCrypto3是官方提供的demo: https://github.com/open-dingtalk/dingtalk-callback-Crypto# 参数说明:'''1.token为应用中事件订阅下的签名token的数据2.aes_key为应用中事件订阅下的加密aes_key的数据3.app_key为应用的应用信息中的AppKey的数据注意:(这块需要具体问题具体分析,可以参考官方文档)1.开发者后台配置的订阅事件为应用级事件推送,此时app_key参数为应用的APP_KEY2.当使用HTTP回调注册接口方式接收钉钉推送的订阅事件时,是以企业为维度推送的,app_key为CorpId'''dingCrypto = DingTalkCrypto.DingTalkCrypto3(token, aes_key, app_key)# 5.解密回调事件decrypt_msg = dingCrypto.getDecryptMsg(msg_signature, timestamp, nonce, encrypt)print(decrypt_msg) # 打印结果: {"EventType":"check_url"}# 6.必须返回一个加密的success,让钉钉服务器进行确认success_map = dingCrypto.getEncryptedMap("success")return success_mapif __name__ == '__main__':api.run(port=6666, debug=True, host='0.0.0.0') # 启动服务# debug=True,改了代码后,不用重启,它会自动重启# 'host='0.0.0.0'可以被所有请求访问到
DingTalkCrypto
需要注意的是:
众所周知,Crypto
是个老坑包了,如果下载不了Crypto
或者说用不了Crypto
类库,那么需要下载pycryptodome
pycrypto
、pycrytodome
和crypto
是一个东西,crypto
在python上面的名字是pycrypto
,它是一个第三方库,但是已经停止更新四年了(截止到2023-02-16),所以不建议安装这个库
下载pycryptodome
库之前需要把之前安装的crypto
库都卸载干净
pip uninstall pycrypto
pip uninstall cryptography
pip uninstall crypto
pip uninstall pycryptodome
pip install pycryptodome
# -*- coding: utf-8 -*-
# 依赖Crypto类库
# API说明
# getEncryptedMap 生成回调处理成功后success加密后返回给钉钉的json数据
# decrypt 用于从钉钉接收到回调请求后import time
import io, base64, binascii, hashlib, string, struct
from random import choice
from Crypto.Cipher import AES"""
@param token 钉钉开放平台上,开发者设置的token
@param encodingAesKey 钉钉开放台上,开发者设置的EncodingAESKey
@param corpId 企业自建应用-事件订阅, 使用appKey企业自建应用-注册回调地址, 使用corpId第三方企业应用, 使用suiteKey
"""
class DingTalkCrypto3:def __init__(self, token, encodingAesKey, key):self.encodingAesKey = encodingAesKeyself.key = keyself.token = tokenself.aesKey = base64.b64decode(self.encodingAesKey + '=')# 生成回调处理完成后的success加密数据def getEncryptedMap(self, content):encryptContent = self.encrypt(content)timeStamp = str(int(time.time()))nonce = self.generateRandomKey(16)sign = self.generateSignature(nonce, timeStamp, self.token,encryptContent)return {'msg_signature':sign,'encrypt':encryptContent,'timeStamp':timeStamp,'nonce':nonce}# 解密钉钉发送的数据def getDecryptMsg(self, msg_signature, timeStamp, nonce, content):"""解密:param content::return:"""sign = self.generateSignature(nonce, timeStamp, self.token,content)print(sign, msg_signature)if msg_signature != sign:raise ValueError('signature check error')content = base64.decodebytes(content.encode('UTF-8')) # 钉钉返回的消息体iv = self.aesKey[:16] # 初始向量aesDecode = AES.new(self.aesKey, AES.MODE_CBC, iv)decodeRes = aesDecode.decrypt(content)#pad = int(binascii.hexlify(decodeRes[-1]),16)pad = int(decodeRes[-1])if pad > 32:raise ValueError('Input is not padded or padding is corrupt')decodeRes = decodeRes[:-pad]l = struct.unpack('!i', decodeRes[16:20])[0]# 获取去除初始向量,四位msg长度以及尾部corpidnl = len(decodeRes)if decodeRes[(20+l):].decode() != self.key:raise ValueError('corpId 校验错误')return decodeRes[20:(20+l)].decode()def encrypt(self, content):"""加密:param content::return:"""msg_len = self.length(content)content = ''.join([self.generateRandomKey(16) , msg_len.decode() , content , self.key])contentEncode = self.pks7encode(content)iv = self.aesKey[:16]aesEncode = AES.new(self.aesKey, AES.MODE_CBC, iv)aesEncrypt = aesEncode.encrypt(contentEncode.encode())return base64.encodebytes(aesEncrypt).decode('UTF-8')# 生成回调返回使用的签名值def generateSignature(self, nonce, timestamp, token, msg_encrypt):print(type(nonce), type(timestamp), type(token), type(msg_encrypt))v = msg_encryptsignList = ''.join(sorted([nonce, timestamp, token, v]))return hashlib.sha1(signList.encode()).hexdigest()def length(self, content):"""将msg_len转为符合要求的四位字节长度:param content::return:"""l = len(content)return struct.pack('>l', l)def pks7encode(self, content):"""安装 PKCS#7 标准填充字符串:param text: str:return: str"""l = len(content)output = io.StringIO()val = 32 - (l % 32)for _ in range(val):output.write('%02x' % val)# print "pks7encode",content,"pks7encode", val, "pks7encode", output.getvalue()return content + binascii.unhexlify(output.getvalue()).decode()def pks7decode(self, content):nl = len(content)val = int(binascii.hexlify(content[-1]), 16)if val > 32:raise ValueError('Input is not padded or padding is corrupt')l = nl - valreturn content[:l]def generateRandomKey(self, size,chars=string.ascii_letters + string.ascii_lowercase + string.ascii_uppercase + string.digits):"""生成加密所需要的随机字符串:param size::param chars::return:"""return ''.join(choice(chars) for i in range(size))if __name__ == '__main__':dingCrypto = DingTalkCrypto3("xxxxxxxx", "xxxxxxxxxxxxxxxxxx", "xxxxxxxxxxx")success = dingCrypto.encrypt("success")print(success)
2.3接口验证
写好接口服务后放到有公网IP的地方运行(直接在python或者conda虚拟python环境下运行py文件、打包成可执行文件执行均可)
将服务接口复制到钉钉应用中的事件与回调中的请求网址中,点击保存即可
需要注意的是:
在开发过程中会测试事件订阅是否完成,高频率的刷新网页可能会导致事件订阅的aes_key和token被刷新或者没刷新但是因为缓存的问题,会有偏差,需要注意事件订阅中的两个值和接口服务代码中的两个值要一一对应起来,如果对应有误的话,保存的时候会报错
相关文章:
钉钉配置事件订阅(Python)
钉钉配置事件订阅 0.需求分析 需要实现钉钉企业通讯录同步至企业微信通讯录,这就需要用到钉钉的事件与回调 1.配置应用 登陆开放平台 https://open-dev.dingtalk.com/去企业内部开发里面,先创建个应用,后面都借用这个应用来调接口 创建完…...
Linux-Udev机制
一:Udev概述 udev 是一个用户空间的设备管理器,用于为事件设置处理程序。作为守护进程, udev 接收的事件主要由 linux 内核生成,这些事件是外部设备产生的物理事件。总之, udev 探测外设和热插拔,将设备控制权传递给内核,例如加载内核模块或设备固件。udev 是一个用户空…...
ERP是什么?中小商户有必要用吗?秦丝、金蝶、管家婆哪家强?
ERP系统刚开始传入中国的时候,基本上只有超大型或大型企业有条件实施,不过最近几年随着小微企业、中小商户的信息化需求不断增长,ERP软件已慢慢被普遍使用。但是仍然有不少中小商户,还没搞清楚ERP到底是什么,看到大家都…...
pytorch离线安装
windows下离线安装pytorch,很多内网机,无法连接外网,只能下载whl文件进行离线安装下载pytorch,地址https://download.pytorch.org/whl/torch_stable.html我是windows,Python37,没有gpu,所以选择…...
数据结构-算法的时间复杂度(1.1)
目录 1. 算法效率 2. 时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3 举例说明: 写在最后: 1. 算法效率 我们该如何判断一个算法的好坏? 衡量一个算法的好坏,是从时间和空间两个维度比较的, 而今天…...
Cygwin安装与Mingw
共同点:window下编译环境 区别:cygwin(gnu windows)模拟Linux编译环境, mingw模拟window编译环境,生成.exe可执行文件 目录 Cygwin安装 一、官网下载 二、双击安装 三、选择安装路径后,到连接方式如图 四、添加连…...
教育舆情监测方案有哪些,TOOM讲解教育舆情的应对与处理?
教育舆情方案是针对教育领域的舆情事件或问题而制定的应对方案。其主要目的是通过有效的信息收集、分析、处理和传播,帮助教育机构或相关组织及时掌握和应对公众舆论的发展趋势,维护良好的舆情形象和声誉,教育舆情监测方案有哪些,…...
c语言操作文件
1、文件缓冲区 文件缓冲区的目的:提高访问效率 提高磁盘使用寿命 刷新就是将当前缓冲区数据全部提交。 不刷新时,程序在崩溃时缓冲区内容无法输出(有些情形会带来错误) 文件缓冲区的四种刷新方式 行刷新(遇到换行符…...
【C语言】初识指针
目录 一、指针是什么 二、指针和指针类型 三、野指针 四、指针运算 五、指针和数组 六、二级指针 七、指针数组 一、指针是什么 指针就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度…...
FFMPEG自学一 音视频解封装
一、音视频包含哪些数据对于一个mp4文件我们可以通过音视频分析软件打开查看内部信息。从两图可以看出mp4文件一般包含 音频流 视频流等。对于上面的字段大致分析如下Format编码方式AVC现在大部分视频都是这种编码方式,即H264。CodecId编码器idavc1H264封装有2种格式…...
HoloLens 2 丨打包丨MRTK丨Unity丨新手教学
HoloLens 2打包流程制作前言开发工具介绍Visual Studio 2019MRTK插件或示例程序下载打包流程介绍Unity操作修改Visual Studio修改Hololens 修改Hololens 密码忘记总结前言 提示:今日功能介绍 使用 MRTK制作hololens 2的打包流程制作的新手教学。 开发工具介绍 这…...
AcWing语法基础课笔记 第四章 C++中的数组
第四章 C中的数组 程序 逻辑 数据,数组是存储数据的强而有力的手段。 ——闫学灿 一维数组 数组的定义 数组的定义方式和变量类似。 数组的初始化 在main函数内部,未初始化的数组中的元素是随机的。 访问数组元素 通过下标访问数…...
UTF小结
运行测试 编辑测试 运行模式:程序集Platform平台选择 Any Platforms编辑模式:程序集Platform平台选择 Editor 特性 Test、UnityTest特性:测试方法需要添加Test或UnityTest特性,测试方法是公有的SetUp、TearDown特性:…...
(考研湖科大教书匠计算机网络)第四章网络层-第六节3:开放最短路径优先OSPF的基本工作原理
获取pdf:密码7281专栏目录首页:【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一:OSPF概述(1)概述(2)细节阐述A:链路状态和代价B:问候分组和邻居表Cÿ…...
积水在线监测仪——积水点、易涝点水位监测设备
一、设备概述 积水在线监测仪是一款用于城市积水点、易涝点等场景的水位监测设备,设备采用电池供电,无需另外供电,安装方便,使用简单。可以时监测水点、易涝点水位情况,当水位数据超过阈值后触发告警上传,…...
DCMM认证机构
一、什么是DCMM DCMM认证,又称为数据管理能力成熟度评估,依据 都是GB/T -《数据管理能力成熟度评估模型》,这是我国首个数据管理领域的国家标准,由国家质量监督检验检疫总局、国家标准化管理委员会于年3月15日正式发布。DCMM认证…...
Golang基于文件魔数判断文件类型
本文介绍基于魔数判断文件类型,涉及文件查找读取内容、文件魔数、字节比较,最后还介绍函数参数的知识。 查找位置 File.Seek()函数可以设置偏移位置,为下一次读或写确定偏移量,具体起点有whence确定:0标识相对文件开始…...
MySQL——索引视图练习题
学生表:Student (Sno, Sname, Ssex , Sage, Sdept) 学号,姓名,性别,年龄,所在系 Sno为主键 课程表:Course (Cno, Cname,) 课程号,课程名 Cno为主键 学生选课表:SC (Sno, Cno, Score)…...
哈希表题目:矩阵置零
文章目录题目标题和出处难度题目描述要求示例数据范围进阶解法一思路和算法代码复杂度分析解法二思路和算法代码复杂度分析解法三思路和算法代码复杂度分析题目 标题和出处 标题:矩阵置零 出处:73. 矩阵置零 难度 3 级 题目描述 要求 给定一个 m…...
HTTP API自动化测试从手工到平台的演变
不管是 Web 系统,还是移动 APP,前后端逻辑的分离设计已经是常态化,相互之间通过 API 调用进行数据交互。在基于 API 约定的开发模式下,如何加速请求 / 响应的 API 测试,让研发人员及早参与到调试中来呢?既然…...
【从零开始学C语言】知识总结一:C语言的基本知识汇总
C语言期末知识点总结 C语言期末试题(附答案)选择题编程题 2022C语言知识点大全【详细、必备】 C语言期末大作业-学生成绩管理系统(完整源码设计报告) C语言期末作业(15个)-货物管理系统、歌曲信息管理系…...
CAD二次开发 添加按钮Ribbon
这篇文章是教大家怎样子创建自己的Ribbon按钮界面(如下图),以下示例代码在CAD2020中运行实现。 背景 创建一个属于自己的Ribbon按钮(如下图) 理解Ribbon、Panel、Tab的关系(如下图)ÿ…...
[RK3568 Android12] 添加自定义启动脚本
1:定义添加的脚本 比如为displayn2k.sh #!/system/bin/sh log "displayn2k.sh begin running" sleep 5 log "displayn2k.sh sleep 8" sleep 5 log "================sleep finished==========================" #remount /system/bin/mount -o …...
API 体系构建
前言 API 是模块或者子系统之间交互的接口定义。好的系统架构离不开好的 API 设计,而一个设计不够完善的 API 则注定会导致系统的后续发展和维护非常困难。在关键环节制定明确的 API 规范有助于 Service 对内提高产品间互通的效率,对外提供一致的使用体…...
RMPE: Regional Multi-Person Pose Estimation (AlphaPose)阅读笔记
区域多人姿态估计 ICCV 2017 论文链接 代码链接 摘要: 野外多人姿态估计具有挑战性。sota人体检测器不可避免存在定位和识别误差,这些误差可能导致依赖人体检测器的单人姿态估计器(SPPE)的失败。本文提出了一种新的区域多人姿态估…...
2月16日昆明面试经历部分考题
2月16日昆明面试部分考题 1.说说em和rem的区别?rpx呢? rem是相对于根元素(HTML)进行计算,而em是相对于当前元素或父元素的字体大小,如果当前文本的字体尺寸没有设置,则相对于浏览器的默认字体…...
ARC140D One to One
ARC140D One to One 题目大意 对于一个长度为nnn的整数序列X(x1,x2,…xn)X(x_1,x_2,\dots x_n)X(x1,x2,…xn),每个元素都在111到nnn之间,令f(X)f(X)f(X)表示以下问题的答案: 有一个nnn个顶点nnn条边的无向图(可能有重边和…...
联合身份验证与Cognito
Hello大家好,我们接下来讨论AWS联合身份验证的内容。 AWS联合身份验证 对于考试,联合身份验证部分是一块非常重要的内容。那什么是联合身份验证,它是做什么用的呢? 联合身份验证,是用来允许AWS外部用户,如…...
day18_常用API之String类丶Object类
String概述 java.lang.String 类代表字符串,String类定义的变量可以用于指向字符串对象,同时String类提供了很多操作字符串的功能,我们可以直接使用。Java 程序中的所有字符串文字(例如“abc”)都为此类的对象 特点:St…...
OSG三维渲染引擎编程学习之五十五:“第五章:OSG场景渲染” 之 “5.13 一维纹理”
目录 第五章 OSG场景渲染 5.13 一维纹理 5.13.1 一维纹理介绍 5.13.2 一维纹理示例 第五章 OSG场景渲染 OSG存在场景树和渲染树,“场景数”的构建在第三章“OSG场景组织”已详细阐明,本章开始...
怎么用linux做网站/百度竞价排名价格查询
本教程将教你如何使用qmake。 从简单的实例开始 让我们假设你刚才完成了您的应用程序的基本实现,您已经创建了以下文件: hello.CPPhello.hmain.cpp首先,用你最喜欢的纯文本编辑器,创建一个名为hello.pro的文件。你需要做的第一件…...
wordpress 魔/推广渠道有哪些
教学反思:三角形的中线与面积法人教版数学八年级上册第11章第1.2节《三角形的高、中线与角平分线》中,我们对三角形的中线描述是“连接三角形顶点和对边中点的线段”,这和前一小节中对边、对角概念的理解提出的要求对应,即学生能识…...
网站建设运营的灵魂是什么/网站关键字排名优化
lambda 传递ref参数有个语法bug,必须要显式书写参数类型。 //如 delegate bool FuncType(ref int num);FuncType func1; func1 num > true; //错 func1 (ref num) > true;//错 func1 (ref int num) > true;//ok//并且,当一个参数书写类型,其…...
招商加盟网站模板html/口碑营销理论
目录 文件操作和IO流复习一、map集合如何实现排序本节任务教学目标教学内容一、File类二、IO流四、字符流1. 输入流2. 输出流五、编码文件操作和IO流 复习 一、map集合如何实现排序 // TODO HashMap - key为一个引用类型(自定义类型)// 定义Hash结构的Map集合Map<Student, I…...
网站上飘窗怎么做/好的竞价账户托管外包
现在基本上做产品的会有一个PMP证件,从结果导向来说,不对口不会有这么大范围的人来考,但是需要因地制宜,在公司内部里,标准程序并不流畅,产品和项目并不规范,关系错综复杂。 而产品经理的职能又…...
wordpress网站搭建/百度域名注册查询
1详细二进制 (主要是用于基本i/o流) #二进制: 1、计算机内部都是2进制 2、数组和ArrayList的区别:ArrayLlsit里面有算法(通用算法,只有当通用算法不行的时候,可以自己写专门算法)…...