python项目对接钉钉SDK的实现


Posted in Python onJuly 15, 2019

 钉钉SDK

对接sdk还是遇到不少问题的

钉钉python版SDK文档地址:https://dingtalk-sdk.readthedocs.io/zh_CN/latest/

钉钉官方服务端文档:https://open-doc.dingtalk.com/microapp/serverapi2

为了避免调试出现不必要的参数错误,前期钉钉配置要做好,血和泪的教训?

准备好下面几个参数

DINGTALK_CORP_ID = "重点:企业应用ID"
# 钉钉微应用
DINGTALK_APP_KEY = "微应用APP_KEY"
DINGTALK_CORP_SECRET = "微应用SERCRET"
# 钉钉移动应用(扫码登录),在移动应用接入菜单里配置
DINGTALK_APP_ID = "移动应用APP_ID"
DINGTALK_APP_SECRET = "移动应用APP_SECRET"
# 钉钉aes加密(随机)
DINGTALK_AES_TOKEN = "43位字符串 a-zA-Z0-9中生成"
DINGTALK_TOKEN = "字符串随便填"

一. 第三方web网站扫码登录1. 流程

钉钉扫码—>判断是否公司员工—>登录

python项目对接钉钉SDK的实现

python项目对接钉钉SDK的实现

关于web系统的两种接入方式,都只能获取到用户的钉钉身份,并不能获取到用户是否在企业中

解决方法:

批量同步钉钉人员信息到数据库,登录后匹配数据库,新进员工通过钉钉回调同步到数据库,回调未生效人工审核后手动更新数据库

由于钉钉开发文档读的不够仔细导致调试频繁出错,所以开发流程我简单说,把细节放大

前端导入钉钉scrpit文件开发扫码页面

<script src="//g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
<script>
var url="http://erp.vaiwan.com/api/v1.0/ding_test";
var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=dingoadwjj1iszubeh9rnf&response_type=code&scope=snsapi_login&state=STATE&redirect_uri='+url);
var obj = DDLogin({
   id:"login_container",
   goto: goto,
   style: "border:none;background-color:#FFFFFF;",
   width : "365",
   height: "400"
 });

var hanndleMessage = function (event) {
    var origin = event.origin;
    console.log("origin", event.origin);
  	//判断是否来自ddLogin扫码事件。
    if( origin == "https://login.dingtalk.com" ) {
      //拿到loginTmpCode后就可以在这里构造跳转链接进行跳转了
      var loginTmpCode = event.data;
      console.log("loginTmpCode", loginTmpCode);
      if(loginTmpCode){
        let uri="https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=dingoadwjj1iszubeh9rnf&response_type=code&scope=snsapi_login&state=STATE&redirect_uri="+url+"&loginTmpCode="+loginTmpCode;
        // 跳转
        location.href = uri;
      }
    }
};

if (typeof window.addEventListener != 'undefined') {
  window.addEventListener('message', hanndleMessage, false);
} else if (typeof window.attachEvent != 'undefined') {
  window.attachEvent('onmessage', hanndleMessage);
}
</script>

配置扫码后跳转页面(就是第一步goto参数里的url),钉钉会自动在跳转页面路由后添加codestate参数

python项目对接钉钉SDK的实现

拿到code请求后端接口

<script src="https://cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script>
<script>
  // location.search: "?code=bb42ec66ed5...&state=STATE"
  let code = location.search.split("&")[0].split("=")[1];
  $.ajax({
    type:"post",
    url:'http://erp.vaiwan.com.cn/api/v1.0/getinfo',
    data:{
      "code":code
    },
    success:function(data){
      console.log(data)
    }
  })
</script>

后端用appid和appsecret获取access_token

import requests
from dingtalk.client import SecretClient

class GetInfoByCode(object):
  def __init__(self):
    self.url = "https://oapi.dingtalk.com/sns"

  def get_access_token(self):
    resp = requests.get(
      url=self.url + "/gettoken",
      params=dict(appid=DINGTALK_APP_ID, appsecret=DINGTALK_APP_SECRET)
    )
    resp = resp.json()
    return resp["access_token"]

用access_token和code获取用户unionid

def get_persistent_code(self, code, token):
    resp = requests.post(
      url="%s/get_persistent_code?access_token=%s" % (self.url, token),
      json=dict(tmp_auth_code=code),
    )
    resp = resp.json()
    return resp["unionid"]

用unionid换取userid

ding_client = SecretClient(corp_id=DINGTALK_CORP_ID,
               corp_secret=DINGTALK_CORP_SECRET)
# 这块提醒下一定要做缓存,由于我之前没做缓存,超过钉钉使用频率而被限制了,修改下上面那句代码
import redis
from dingtalk.storage.kvstorage import KvStorage

# redis做缓存
redis_ding = redis.StrictRedis(REDIS_HOST, REDIS_PORT, db=0)
# 划重点,因为我是第三方web网站,没有使用E应用,所以我的corp_id填的是微应用的id,而不是企业应用ID(corp_id)
ding_client = SecretClient(
  corp_id=DINGTALK_APP_KEY,
  corp_secret=DINGTALK_CORP_SECRET,
  storage=KvStorage(redis_ding))

userid = ding_client.user.get_userid_by_unionid(unionid)

userid换取user详情

user = ding_client.user.get(userid["userid"])

二. 加解密

应用场景: 钉钉回调

事件流程:注册回调事件—>处理事件内容(新增员工、更新部门等),返回钉钉加密json

python项目对接钉钉SDK的实现

就是说你调用注册事件API,钉钉会给你需要注册的url发送一个post请求,你得按要求返回success,否则注册失败

1.注册事件

python项目对接钉钉SDK的实现

再看看什么是success的加密json数据

python项目对接钉钉SDK的实现

如果你没有看懂的话,很巧 我也看不懂…

json数据包含这些参数

python项目对接钉钉SDK的实现

时间戳和nonce容易拿,问题在于encryptmsg_signature的生成

因为钉钉没有提供python的加解密库,所以看到这块我有点懵

python项目对接钉钉SDK的实现

钉钉的文档看不太懂…

python项目对接钉钉SDK的实现

百度搜了下 发现有人自己封装了这个库,我看了下名字XXXcryptocrypto这个单词好像在sdk里边见过。

看了sdk目录,果然,虽然钉钉sdk虽然没写关于加解密的内容,但是它其实已经封装了这个库

使用

from dingtalk.crypto import DingTalkCrypto

class Crypto(object):
  def __init__(self):
    # 随便填的字符串
    self.token = DINGTALK_TOKEN
    # 自己生成的43位随机字符串
    self.aes_key = DINGTALK_AES_TOKEN
    # 钉钉企业ID
    self.corpid = DINGTALK_APP_ID
    self.crypto = DingTalkCrypto(
      token=self.token,
      encoding_aes_key=self.aes_key,
      corpid_or_suitekey=self.corpid
    )

说明

原本是不需要自己封装的。看dingtalk的源码

python项目对接钉钉SDK的实现

在开始定义ding_client的时候,只需要填参数就可以,经过我反复的调试,发现因为我开发的是企业内部应用-微应用,而不是E应用,所以这里填的corp_id其实是微应用app_key,参数不正确导致后边调试一直返回文本非success错误,为了不与上边填的参数冲突,所以自己封装Crypto类,填写的corp_id是企业应用id

封装好Crypto这个类,就可以调用啦, 先测试返回给钉钉的结果是否正确

from ... import Crypto

class EventCallBack(Resource):
  def __init__(self):
    self.crypto = Crypto().crypto
    
  def post(self):
    # 返回加密success
    result = self.crypto.encrypt_message(
      msg="success",
      nonce="123456",
      timestamp=int(time.time()*1000)
    )
    return result

事情没这么容易结束…

这个post方法你需要对回调事件作出处理而不是仅仅返回success,所以最终需要这样, 对结果进行处理

def post(self):
    po = request.json["encrypt"]
    data = request.values
    # 返回加密success
    result = self.crypto.encrypt_message(
      msg="success",
      nonce="123456",
      timestamp=int(time.time()*1000)
    )
    # 获取到事件及其他信息
    callback = self.crypto.decrypt_encrypt_str(
      encrypt_str=po,
      nonce=data["nonce"],
      timestamp=data["timestamp"],
      signature=data["signature"]
    )
    # demjson可以把字符串转为字典,否则取值失败
    # 安装 pip install demjson
    callback = demjson.decode(callback)
    # 判断事件类型,作出相应回调
    if callback["EventType"] == "user_add_org":
      # 添加员工
      ...
    elif callback["EventType"] == "org_dept_create":
      # 添加部门
      ...
    return result

三. 调试

# 钉钉内网穿透工具,主要作用是方便本地调试需要回调域名的接口
# 地址: https://open-doc.dingtalk.com/microapp/debug/ucof2g
可以后台启动

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python编程-将Python程序转化为可执行程序[整理]
Apr 09 Python
解析Python中的二进制位运算符
May 13 Python
python虚拟环境virtualenv的安装与使用
Sep 21 Python
基于Python socket的端口扫描程序实例代码
Feb 09 Python
Python cookbook(数据结构与算法)同时对数据做转换和换算处理操作示例
Mar 23 Python
python批量修改文件编码格式的方法
May 31 Python
PyCharm代码整体缩进,反向缩进的方法
Jun 25 Python
python按修改时间顺序排列文件的实例代码
Jul 25 Python
python脚本实现音频m4a格式转成MP3格式的实例代码
Oct 09 Python
用Python开发app后端有优势吗
Jun 29 Python
python 写一个文件分发小程序
Dec 05 Python
python中reload重载实例用法
Dec 15 Python
用Python识别人脸,人种等各种信息
Jul 15 #Python
django中账号密码验证登陆功能的实现方法
Jul 15 #Python
python tkinter窗口最大化的实现
Jul 15 #Python
在pycharm下设置自己的个性模版方法
Jul 15 #Python
Pycharm新建模板默认添加个人信息的实例
Jul 15 #Python
在python中将list分段并保存为array类型的方法
Jul 15 #Python
详解Python对JSON中的特殊类型进行Encoder
Jul 15 #Python
You might like
php下实现农历日历的代码
2007/03/07 PHP
PHP mail 通过Windows的SMTP发送邮件失败的解决方案
2009/05/27 PHP
php ajax 静态分页过程形式
2011/09/02 PHP
PHP使用gmdate实现将一个UNIX 时间格式化成GMT文本的方法
2015/03/19 PHP
PHP性能分析工具XHProf安装使用教程
2015/05/13 PHP
PHP简单实现冒泡排序的方法
2016/12/26 PHP
js 数组实现一个类似ruby的迭代器
2009/10/27 Javascript
jQuery select操作控制方法小结
2010/05/26 Javascript
js动态创建表格,删除行列的小例子
2013/07/20 Javascript
查看大图功能代码jquery版
2013/11/05 Javascript
JS调用页面表格导出excel示例代码
2014/03/18 Javascript
JavaScript截取字符串的Slice、Substring、Substr函数详解和比较
2014/03/20 Javascript
jQuery.extend()、jQuery.fn.extend()扩展方法示例详解
2014/05/08 Javascript
轻松创建nodejs服务器(3):代码模块化
2014/12/18 NodeJs
JavaScript中实现继承的三种方式和实例
2015/01/29 Javascript
javascript实现点击按钮让DIV层弹性移动的方法
2015/02/24 Javascript
jquery实现简单实用的弹出层效果代码
2015/10/15 Javascript
基于MVC5和Bootstrap的jQuery TreeView树形控件(一)之数据支持json字符串、list集合
2016/08/11 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
2016/11/06 Javascript
seajs模块压缩问题与解决方法实例分析
2017/10/10 Javascript
vue中添加mp3音频文件的方法
2018/03/02 Javascript
vue中keep-alive组件的入门使用教程
2019/06/06 Javascript
bootstrap datepicker的基本使用教程
2019/07/09 Javascript
在Vue项目中用fullcalendar制作日程表的示例代码
2019/08/04 Javascript
JavaScript中判断为整数的多种方式及保留两位小数的方法
2019/09/09 Javascript
vue实现一个6个输入框的验证码输入组件功能的实例代码
2020/06/29 Javascript
[50:27]OG vs LGD 2018国际邀请赛淘汰赛BO3 第一场 8.26
2018/08/30 DOTA
总结Python中逻辑运算符的使用
2015/05/13 Python
Python list与NumPy array 区分详解
2019/11/06 Python
五个2015 年最佳HTML5 框架
2015/11/11 HTML / CSS
澳大利亚领先的在线葡萄酒零售商:Get Wines Direct
2018/03/27 全球购物
C有"按引用传递"吗
2016/09/06 面试题
团员的自我评价
2013/12/01 职场文书
回门宴父母答谢词
2014/01/26 职场文书
监察建议书
2015/02/04 职场文书
2015教师年度考核评语
2015/03/25 职场文书