python項(xiàng)目對(duì)接釘釘SDK的實(shí)現(xiàn)
釘釘SDK
對(duì)接sdk還是遇到不少問(wèn)題的
釘釘python版SDK文檔地址:https://dingtalk-sdk.readthedocs.io/zh_CN/latest/
釘釘官方服務(wù)端文檔:https://open-doc.dingtalk.com/microapp/serverapi2
為了避免調(diào)試出現(xiàn)不必要的參數(shù)錯(cuò)誤,前期釘釘配置要做好,血和淚的教訓(xùn)😭
準(zhǔn)備好下面幾個(gè)參數(shù)
DINGTALK_CORP_ID = "重點(diǎn):企業(yè)應(yīng)用ID" # 釘釘微應(yīng)用 DINGTALK_APP_KEY = "微應(yīng)用APP_KEY" DINGTALK_CORP_SECRET = "微應(yīng)用SERCRET" # 釘釘移動(dòng)應(yīng)用(掃碼登錄),在移動(dòng)應(yīng)用接入菜單里配置 DINGTALK_APP_ID = "移動(dòng)應(yīng)用APP_ID" DINGTALK_APP_SECRET = "移動(dòng)應(yīng)用APP_SECRET" # 釘釘aes加密(隨機(jī)) DINGTALK_AES_TOKEN = "43位字符串 a-zA-Z0-9中生成" DINGTALK_TOKEN = "字符串隨便填"
一. 第三方web網(wǎng)站掃碼登錄1. 流程
釘釘掃碼—>判斷是否公司員工—>登錄


關(guān)于web系統(tǒng)的兩種接入方式,都只能獲取到用戶的釘釘身份,并不能獲取到用戶是否在企業(yè)中
解決方法:
批量同步釘釘人員信息到數(shù)據(jù)庫(kù),登錄后匹配數(shù)據(jù)庫(kù),新進(jìn)員工通過(guò)釘釘回調(diào)同步到數(shù)據(jù)庫(kù),回調(diào)未生效人工審核后手動(dòng)更新數(shù)據(jù)庫(kù)
由于釘釘開發(fā)文檔讀的不夠仔細(xì)導(dǎo)致調(diào)試頻繁出錯(cuò),所以開發(fā)流程我簡(jiǎn)單說(shuō),把細(xì)節(jié)放大
前端導(dǎo)入釘釘scrpit文件開發(fā)掃碼頁(yè)面
<script src="http://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);
//判斷是否來(lái)自ddLogin掃碼事件。
if( origin == "https://login.dingtalk.com" ) {
//拿到loginTmpCode后就可以在這里構(gòu)造跳轉(zhuǎn)鏈接進(jìn)行跳轉(zhuǎn)了
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;
// 跳轉(zhuǎn)
location.href = uri;
}
}
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', hanndleMessage, false);
} else if (typeof window.attachEvent != 'undefined') {
window.attachEvent('onmessage', hanndleMessage);
}
</script>
配置掃碼后跳轉(zhuǎn)頁(yè)面(就是第一步goto參數(shù)里的url),釘釘會(huì)自動(dòng)在跳轉(zhuǎn)頁(yè)面路由后添加code和state參數(shù)

拿到code請(qǐng)求后端接口
<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)
# 這塊提醒下一定要做緩存,由于我之前沒(méi)做緩存,超過(guò)釘釘使用頻率而被限制了,修改下上面那句代碼
import redis
from dingtalk.storage.kvstorage import KvStorage
# redis做緩存
redis_ding = redis.StrictRedis(REDIS_HOST, REDIS_PORT, db=0)
# 劃重點(diǎn),因?yàn)槲沂堑谌絯eb網(wǎng)站,沒(méi)有使用E應(yīng)用,所以我的corp_id填的是微應(yīng)用的id,而不是企業(yè)應(yīng)用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"])
二. 加解密
應(yīng)用場(chǎng)景: 釘釘回調(diào)
事件流程:注冊(cè)回調(diào)事件—>處理事件內(nèi)容(新增員工、更新部門等),返回釘釘加密json

就是說(shuō)你調(diào)用注冊(cè)事件API,釘釘會(huì)給你需要注冊(cè)的url發(fā)送一個(gè)post請(qǐng)求,你得按要求返回success,否則注冊(cè)失敗
1.注冊(cè)事件

再看看什么是success的加密json數(shù)據(jù)

如果你沒(méi)有看懂的話,很巧 我也看不懂…
json數(shù)據(jù)包含這些參數(shù)

時(shí)間戳和nonce容易拿,問(wèn)題在于encrypt和msg_signature的生成
因?yàn)獒斸敍](méi)有提供python的加解密庫(kù),所以看到這塊我有點(diǎn)懵

釘釘?shù)奈臋n看不太懂…

百度搜了下 發(fā)現(xiàn)有人自己封裝了這個(gè)庫(kù),我看了下名字XXXcrypto,crypto這個(gè)單詞好像在sdk里邊見過(guò)。
看了sdk目錄,果然,雖然釘釘sdk雖然沒(méi)寫關(guān)于加解密的內(nèi)容,但是它其實(shí)已經(jīng)封裝了這個(gè)庫(kù)
使用
from dingtalk.crypto import DingTalkCrypto
class Crypto(object):
def __init__(self):
# 隨便填的字符串
self.token = DINGTALK_TOKEN
# 自己生成的43位隨機(jī)字符串
self.aes_key = DINGTALK_AES_TOKEN
# 釘釘企業(yè)ID
self.corpid = DINGTALK_APP_ID
self.crypto = DingTalkCrypto(
token=self.token,
encoding_aes_key=self.aes_key,
corpid_or_suitekey=self.corpid
)
說(shuō)明
原本是不需要自己封裝的??磀ingtalk的源碼

在開始定義ding_client的時(shí)候,只需要填參數(shù)就可以,經(jīng)過(guò)我反復(fù)的調(diào)試,發(fā)現(xiàn)因?yàn)槲议_發(fā)的是企業(yè)內(nèi)部應(yīng)用-微應(yīng)用,而不是E應(yīng)用,所以這里填的corp_id其實(shí)是微應(yīng)用app_key,參數(shù)不正確導(dǎo)致后邊調(diào)試一直返回文本非success錯(cuò)誤,為了不與上邊填的參數(shù)沖突,所以自己封裝Crypto類,填寫的corp_id是企業(yè)應(yīng)用id
封裝好Crypto這個(gè)類,就可以調(diào)用啦, 先測(cè)試返回給釘釘?shù)慕Y(jié)果是否正確
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
事情沒(méi)這么容易結(jié)束…
這個(gè)post方法你需要對(duì)回調(diào)事件作出處理而不是僅僅返回success,所以最終需要這樣, 對(duì)結(jié)果進(jìn)行處理
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可以把字符串轉(zhuǎn)為字典,否則取值失敗
# 安裝 pip install demjson
callback = demjson.decode(callback)
# 判斷事件類型,作出相應(yīng)回調(diào)
if callback["EventType"] == "user_add_org":
# 添加員工
...
elif callback["EventType"] == "org_dept_create":
# 添加部門
...
return result
三. 調(diào)試
# 釘釘內(nèi)網(wǎng)穿透工具,主要作用是方便本地調(diào)試需要回調(diào)域名的接口 # 地址: https://open-doc.dingtalk.com/microapp/debug/ucof2g 可以后臺(tái)啟動(dòng)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python程序自動(dòng)以管理員權(quán)限運(yùn)行的實(shí)現(xiàn)方法
Windows上為了安全起見,python語(yǔ)言啟動(dòng)的應(yīng)用程序默認(rèn)都不會(huì)使用admin管理員權(quán)限,但是在有些情況下我們又需要使用管理員權(quán)限啟動(dòng)應(yīng)用,這篇文章主要給大家介紹了關(guān)于Python程序自動(dòng)以管理員權(quán)限運(yùn)行的實(shí)現(xiàn)方法,需要的朋友可以參考下2023-11-11
nginx黑名單和django限速,最簡(jiǎn)單的防惡意請(qǐng)求方法分享
今天小編就為大家分享一篇nginx黑名單和django限速,最簡(jiǎn)單的防惡意請(qǐng)求方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08
Python format補(bǔ)0的實(shí)現(xiàn)方法
對(duì)于一些數(shù)字的處理,我們可能需要讓它們滿足一定格式的要求,本文主要介紹了Python format補(bǔ)0的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
python實(shí)現(xiàn)的登錄與提交表單數(shù)據(jù)功能示例
這篇文章主要介紹了python實(shí)現(xiàn)的登錄與提交表單數(shù)據(jù)功能,結(jié)合實(shí)例形式分析了Python表單登錄相關(guān)的請(qǐng)求與響應(yīng)操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-09-09
peewee創(chuàng)建連接前的前置操作wireshark抓包實(shí)現(xiàn)
這篇文章主要為大家介紹了peewee創(chuàng)建連接前的前置操作wireshark?抓包實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
Python配置pip國(guó)內(nèi)鏡像源的實(shí)現(xiàn)
這篇文章主要介紹了Python配置pip國(guó)內(nèi)鏡像源的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08

