当前位置: 首页 > news >正文

钉钉配置事件订阅(Python)

钉钉配置事件订阅

0.需求分析

需要实现钉钉企业通讯录同步至企业微信通讯录,这就需要用到钉钉的事件与回调

1.配置应用

登陆开放平台

https://open-dev.dingtalk.com/

去企业内部开发里面,先创建个应用,后面都借用这个应用来调接口

在这里插入图片描述

创建完成应用后进入应用,找到下面红框内的数据,后面会用到

在这里插入图片描述
进入应用中的事件与回调,自动生成aes_keytoken,然后保存就好这两个数据

在这里插入图片描述

2.服务开发

2.1请求验证

配置上面1.配置应用中的事件订阅下的请求网址如http://yourserver/api/callback时,需要把这个网址的接口开发好。钉钉会发送一个类似下面这样的请求:

一个POST类型的http请求,携带了部分url参数和json的加密参数

请求的路径参数包括signaturemsg_signaturetimestampnonce,请求体只有一个encryptjson

curl -X 'POST' \'http://yourserver:80/api/callback?signature=369beedea8d1c8d1ad18936e827d29d0c8415baf&msg_signature=369beedea8d1c8d1ad18936e827d29d0c8415baf&timestamp=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

pycryptopycrytodomecrypto是一个东西,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特性&#xff1a…...

(考研湖科大教书匠计算机网络)第四章网络层-第六节3:开放最短路径优先OSPF的基本工作原理

获取pdf:密码7281专栏目录首页:【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一:OSPF概述(1)概述(2)细节阐述A:链路状态和代价B:问候分组和邻居表C&#xff…...

积水在线监测仪——积水点、易涝点水位监测设备

一、设备概述 积水在线监测仪是一款用于城市积水点、易涝点等场景的水位监测设备,设备采用电池供电,无需另外供电,安装方便,使用简单。可以时监测水点、易涝点水位情况,当水位数据超过阈值后触发告警上传,…...

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 测试,让研发人员及早参与到调试中来呢?既然…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...