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实现的udp协议Server和Client代码实例
Jun 04 Python
python实现的登录和操作开心网脚本分享
Jul 09 Python
解决Python运行文件出现out of memory框的问题
Dec 03 Python
python3实现斐波那契数列(4种方法)
Jul 15 Python
python GUI图形化编程wxpython的使用
Jul 19 Python
python脚本执行CMD命令并返回结果的例子
Aug 14 Python
Flask框架请求钩子与request请求对象用法实例分析
Nov 07 Python
Python中bisect的使用方法
Dec 31 Python
python爬虫基础之urllib的使用
Dec 31 Python
Django模型层实现多表关系创建和多表操作
Jul 21 Python
Python中的程序流程控制语句
Feb 24 Python
pandas中pd.groupby()的用法详解
Jun 16 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
Syphon 虹吸式咖啡壶冲煮–拨动法
2021/03/03 冲泡冲煮
PHP里的单例类写法实例
2015/06/25 PHP
高质量PHP代码的50个实用技巧必备(下)
2016/01/22 PHP
PHP判断用户是否已经登录(跳转到不同页面或者执行不同动作)
2016/09/22 PHP
PHP实现导出excel数据的类库用法示例
2016/10/15 PHP
PHP基于迭代实现文件夹复制、删除、查看大小等操作的方法
2017/08/11 PHP
检查输入的是否是数字使用keyCode配合onkeypress事件
2014/01/23 Javascript
JS正则表达式验证数字代码
2014/01/28 Javascript
jquery实现的图片点击滚动效果
2014/04/29 Javascript
jQuery实现在最后一个元素之前插入新元素的方法
2015/07/18 Javascript
基于MVC5和Bootstrap的jQuery TreeView树形控件(一)之数据支持json字符串、list集合
2016/08/11 Javascript
浅谈js中的引用和复制(传值和传址)
2016/09/18 Javascript
vue.js中指令Directives详解
2017/03/20 Javascript
ES6生成器用法实例分析
2017/04/10 Javascript
mockjs,json-server一起搭建前端通用的数据模拟框架教程
2017/12/18 Javascript
Vue filter格式化时间戳时间成标准日期格式的方法
2018/09/16 Javascript
koa源码中promise的解读
2018/11/13 Javascript
七行JSON代码把你的网站变成移动应用过程详解
2019/07/09 Javascript
JS highcharts动态柱状图原理及实现
2020/10/16 Javascript
Python 出现错误TypeError: ‘NoneType’ object is not iterable解决办法
2017/01/12 Python
Pycharm学习教程(4) Python解释器的相关配置
2017/05/03 Python
python中reload(module)的用法示例详解
2017/09/15 Python
python中copy()与deepcopy()的区别小结
2018/08/03 Python
详解pandas赋值失败问题解决
2020/11/29 Python
浅析HTML5:'data-'属性的作用
2018/01/23 HTML / CSS
法国最大的在线眼镜店:EasyLunettes
2019/08/26 全球购物
机械专业应届生求职信
2013/12/12 职场文书
项目经理任命书
2014/06/04 职场文书
北京奥运会主题口号
2014/06/13 职场文书
小学生田径运动会广播稿
2014/09/11 职场文书
学生顶撞老师的检讨书
2014/09/17 职场文书
乡镇务虚会发言材料
2014/10/20 职场文书
学校捐书倡议书
2015/04/27 职场文书
外出学习心得体会范文
2016/01/18 职场文书
Vue中foreach数组与js中遍历数组的写法说明
2021/06/05 Vue.js
ROS系统将python包编译为可执行文件的简单步骤
2021/07/25 Python