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图算法实例分析
Aug 13 Python
pyqt5自定义信号实例解析
Jan 31 Python
Django 连接sql server数据库的方法
Jun 30 Python
解决tensorflow模型参数保存和加载的问题
Jul 26 Python
将pip源更换到国内镜像的详细步骤
Apr 07 Python
代码实例讲解python3的编码问题
Jul 08 Python
使用Python实现跳一跳自动跳跃功能
Jul 10 Python
Django文件存储 自己定制存储系统解析
Aug 02 Python
python 叠加等边三角形的绘制的实现
Aug 14 Python
django queryset 去重 .distinct()说明
May 19 Python
python 获取字典特定值对应的键的实现
Sep 29 Python
Pycharm如何自动生成头文件注释
Nov 14 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框架Laravel的小技巧两则
2015/02/10 PHP
PHP入门教程之表单与验证实例详解
2016/09/11 PHP
实例介绍PHP删除数组中的重复元素
2019/03/03 PHP
laravel-admin select框默认选中的方法
2019/10/03 PHP
javascript代码编写需要注意的7个小细节小结
2011/09/21 Javascript
JavaScript 命名空间 使用介绍
2013/08/29 Javascript
使用jquery.upload.js实现异步上传示例代码
2014/07/29 Javascript
Js中使用hasOwnProperty方法检索ajax响应对象的例子
2014/12/08 Javascript
Angularjs编写KindEditor,UEidtor,jQuery指令
2015/01/28 Javascript
解析JavaScript的ES6版本中的解构赋值
2015/07/28 Javascript
常用的JQuery函数及功能小结
2016/03/24 Javascript
Laravel中常见的错误与解决方法小结
2016/08/30 Javascript
js实现上传图片预览方法
2016/10/25 Javascript
基于bootstrap-datetimepicker.js不支持IE8的快速解决方法
2016/11/07 Javascript
ES6中Math对象的部分扩展
2017/02/20 Javascript
jQuery模拟淘宝购物车功能
2017/02/27 Javascript
[47:52]DOTA2-DPC中国联赛正赛 iG vs LBZS BO3 第二场 3月4日
2021/03/11 DOTA
Python操作MongoDB数据库PyMongo库使用方法
2015/04/27 Python
总结python爬虫抓站的实用技巧
2016/08/09 Python
Python实现二分查找与bisect模块详解
2017/01/13 Python
flask框架视图函数用法示例
2018/07/19 Python
浅谈Python的list中的选取范围
2018/11/12 Python
Python中查看变量的类型内存地址所占字节的大小
2019/06/26 Python
python使用装饰器作日志处理的方法
2019/07/11 Python
Python搭建代理IP池实现存储IP的方法
2019/10/27 Python
Python使用Selenium实现淘宝抢单的流程分析
2020/06/23 Python
matplotlib基础绘图命令之bar的使用方法
2020/08/13 Python
HTML5利用约束验证API来检查表单的输入数据的代码实例
2016/12/20 HTML / CSS
雅诗兰黛美国官网:Estee Lauder美国
2016/07/21 全球购物
工程造价与财务管理专业应届生求职信
2013/10/06 职场文书
化学专业毕业生自荐信
2013/11/15 职场文书
业务员岗位职责
2013/11/16 职场文书
编辑找工作求职信范文
2013/12/16 职场文书
2014年发展党员工作总结
2014/11/12 职场文书
李强为自己工作观后感
2015/06/11 职场文书
病假条格式范文
2015/08/17 职场文书