[Python] 為企業網站應用接入釘釘鑒權,實現釘釘客戶端內自動免登授權,瀏覽器中手動釘釘授權登錄兩種邏輯。
操作步驟
-
企業內部獲得
開發者權限
,沒有的話先申請。 -
訪問 釘釘開放平臺-應用開發 創建一個
企業內部應用-釘釘應用
。
-
打開應用詳情頁,獲取
Client ID
、Client Secret
、CorpId
備用,獲取方式如下圖所示。
-
編寫代碼,搭建相應服務(見下方示例代碼)
示例代碼(以Flask
作為后端):
- templates/auth.html
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>auth</title><script src="https://g.alicdn.com/dingding/dingtalk-jsapi/3.0.25/dingtalk.open.js"></script><script>// 檢查是否在釘釘環境中function isDingTalk() {return /DingTalk/.test(navigator.userAgent);}if (isDingTalk()) {dd.ready(function () {dd.runtime.permission.requestAuthCode({corpId: "dingxxxxxxxxxx", // 企業idonSuccess: function (info) {console.log(info);location.href = "/demo/oauth_redirect?code=" + info.code + "&url=" + location.href;}});});} else {location.href = "/demo/oauth_redirect?url=" + location.href;}</script>
</head>
<body>
</body>
</html>
- 需修改:
corpId: "dingxxxxxxxxxx"
替換為真實的CorpId
- 代碼邏輯:若在釘釘端內,則借助釘釘免登碼完成登錄。反之,則跳轉釘釘授權頁面進行授權登錄(授權頁面重定向由后端控制,當然直接寫在前端也可以)
- app.py
# -*- coding: utf-8 -*-
# Author: 薄荷你玩
import glob
import html
import json
import os
import random
import re
import time
import traceback
from datetime import datetime
from typing import List, Union
from flask import Flask, request, jsonify, Response, render_template, make_response, session
from utils import dingtalk_apiapp = Flask(__name__, static_folder='static')# 設置一個密鑰用于加密會話數據
app.secret_key = '123456'@app.after_request
def add_cors_headers(response):response.headers['Access-Control-Allow-Origin'] = '*' # 允許所有來源的跨域請求response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS' # 允許的 HTTP 方法response.headers['Access-Control-Allow-Headers'] = '*' # 允許的請求頭return response@app.route("/demo")
def demo():if 'user' in session:return render_template('index.html', user=session['user'])# <span style="float: right; display: flex; align-items: center; gap: 5px;">你好,{{user.name}} <img src="{{user.avatar}}" width="25"/></span>return render_template('auth.html')@app.route("/demo/oauth_redirect")
def demo_oauth_redirect():code = request.args.get("code")url = request.args.get("url")if not code:# 重定向到釘釘授權登錄頁redirect_uri = url.split("demo")[0] + "demo/oauth-web"client_id = "dingyyyyyyyyyy" # Client IDreturn app.redirect(f"https://login.dingtalk.com/oauth2/auth?redirect_uri={redirect_uri}&response_type=code&client_id={client_id}&scope=openid&state={url}&prompt=consent")else:user_info = dingtalk_api.x_get_user_info_by_app_code(code)if user_info['success']:session['user'] = user_info['data']return app.redirect(url)else:return user_info["msg"]@app.route("/demo/oauth-web")
def demo_oauth_web():""" 釘釘回調URL,配置到釘釘開發平臺 """code = request.args.get("code")state = request.args.get("state")user_info = dingtalk_api.x_get_user_info_by_web_code(code)if user_info['success']:session['user'] = user_info['data']return app.redirect(state)return user_info["msg"]@app.errorhandler(500)
def internal_server_error(error):# 獲取完整的 traceback 信息traceback_info = traceback.format_exc()# 返回具體的錯誤內容和完整的 tracebackresponse = result_map(500, False, str(error), traceback_info)return jsonify(response), 500if __name__ == '__main__':app.run(host="0.0.0.0", port=5000)
- 需修改:
client_id = "dingyyyyyyyyyy"
替換為真實的Client ID
- utils/dingtalk_api.py
# -*- coding: utf-8 -*-
# Author: 薄荷你玩
# Date: 2025/04/07import requestsDINGTALK_DOMAIN = "https://api.dingtalk.com"
CorpId = "dingxxxxxxxxxx" # 企業ID
ClientId = "dingyyyyyyyyyy" # Client ID
ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Client Secretdef user_info(name, avatar, unionid):return {"success": True,"data": {"name": name,"avatar": avatar,"unionid": unionid}}def get_user_token_by_web_code(code):"""獲取用戶Token--access_token,根據web端釘釘授權code:param code::return:"""url = DINGTALK_DOMAIN + f"/v1.0/oauth2/userAccessToken"headers = {"Content-Type": "application/json"}data = {"clientId": ClientId,"clientSecret": ClientSecret,"code": code,"refreshToken": "","grantType": "authorization_code"}response = requests.post(url, json=data, headers=headers)res = response.json()print(res)return resdef get_user_info_by_access_token(access_token):"""獲取用戶通訊錄個人信息:param access_token::return:"""url = DINGTALK_DOMAIN + f"/v1.0/contact/users/me"headers = {"Content-Type": "application/json","x-acs-dingtalk-access-token": access_token}response = requests.get(url, headers=headers)res = response.json()print(res)return resdef x_get_user_info_by_web_code(code):res = get_user_token_by_web_code(code)if "accessToken" in res.keys():res = get_user_info_by_access_token(res["accessToken"])if "nick" in res.keys():return user_info(name=res['nick'], avatar=res['avatarUrl'], unionid=res['unionId'])return {"success": False, "msg": res}# 釘釘企業內部免登
def get_access_token():url = DINGTALK_DOMAIN + f"/v1.0/oauth2/{CorpId}/token"headers = {"Content-Type": "application/json"}data = {"client_id": ClientId,"client_secret": ClientSecret,"grant_type": "client_credentials"}response = requests.post(url, json=data, headers=headers)res = response.json()print(res)return resdef get_user_id_by_code(access_token, code):"""通過免登碼獲取用戶userid(v2)"""url = f"https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token={access_token}"headers = {"Content-Type": "application/json"}data = {"code": code}response = requests.post(url, json=data, headers=headers)res = response.json()print(res)return resdef get_user_info_by_user_id(access_token, userid):"""通過免登碼獲取用戶userid(v2)"""url = f"https://oapi.dingtalk.com/topapi/v2/user/get?access_token={access_token}"headers = {"Content-Type": "application/json"}data = {"userid": userid}response = requests.post(url, json=data, headers=headers)res = response.json()print(res)return resdef x_get_user_info_by_app_code(code):access_token = get_access_token()['access_token']res = get_user_id_by_code(access_token, code)if "result" in res.keys():user_id = res["result"]["userid"]res = get_user_info_by_user_id(access_token, user_id)if "result" in res.keys():return user_info(name=res['result']['name'], avatar=res['result']['avatar'],unionid=res['result']['unionid'])return {"success": False, "msg": res}if __name__ == '__main__':# res = x_get_user_info_by_app_code("{釘釘端內-免登碼}")res = x_get_user_info_by_web_code("{釘釘web授權碼}")print(res)
- 需修改:
替換為真實的ID或秘鑰CorpId = "dingxxxxxxxxxx" # 企業ID ClientId = "dingyyyyyyyyyy" # Client ID ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Client Secret
- 配置回調域名,如下圖所示,填寫用戶授權后的回調地址(如:http://192.168.2.1:5000/demo/oauth-web),實際使用中換成正式的服務域名。
- 配置完成后,釘釘內訪問
/demo
(如:http://192.168.2.1:5000/demo)即可自動登錄(獲取姓名和頭像等信息);瀏覽器訪問會自動跳轉釘釘授權登錄頁面,授權后完成登錄。