[Python] 企業內部應用接入釘釘登錄,端內免登錄+瀏覽器授權登錄

[Python] 為企業網站應用接入釘釘鑒權,實現釘釘客戶端內自動免登授權,瀏覽器中手動釘釘授權登錄兩種邏輯。
在這里插入圖片描述

操作步驟

  1. 企業內部獲得 開發者權限,沒有的話先申請。

  2. 訪問 釘釘開放平臺-應用開發 創建一個 企業內部應用-釘釘應用
    在這里插入圖片描述

  3. 打開應用詳情頁,獲取 Client IDClient SecretCorpId 備用,獲取方式如下圖所示。
    在這里插入圖片描述

  4. 編寫代碼,搭建相應服務(見下方示例代碼)


示例代碼(以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)
  • 需修改:
    CorpId = "dingxxxxxxxxxx"  # 企業ID
    ClientId = "dingyyyyyyyyyy"  # Client ID
    ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  # Client Secret
    
    替換為真實的ID或秘鑰

  1. 配置回調域名,如下圖所示,填寫用戶授權后的回調地址(如:http://192.168.2.1:5000/demo/oauth-web),實際使用中換成正式的服務域名。
    在這里插入圖片描述
  2. 配置完成后,釘釘內訪問 /demo (如:http://192.168.2.1:5000/demo)即可自動登錄(獲取姓名和頭像等信息);瀏覽器訪問會自動跳轉釘釘授權登錄頁面,授權后完成登錄。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77653.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77653.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77653.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[藍橋杯 2023 國 Python A] 整數變換

P10985 [藍橋杯 2023 國 Python A] 整數變換 題目背景 建議使用 PyPy3 提交本題。 題目描述 小藍有一個整數 n n n。每分鐘&#xff0c;小藍的數都會發生變化&#xff0c;變為上一分鐘的數 減去上一分鐘的數的各個數位和。 例如&#xff0c;如果小藍開始時的數為 23 23 …

【Linux】TCP_Wrappers+iptables實現堡壘機功能

規劃 顯示jumpserver的簡單功能&#xff0c;大致的網絡拓撲圖如下 功能規劃 & 拓撲結構 JumpServer&#xff08;堡壘機&#xff09;主要功能&#xff1a; 對訪問目標服務器進行統一入口控制&#xff08;例如 nginx、mysql、redis&#xff09;。使用 iptables 做 NAT 轉…

用HTML和CSS繪制佩奇:我不是佩奇

在這篇博客中&#xff0c;我將解析一個完全使用HTML和CSS繪制的佩奇(Pig)形象。這個項目展示了CSS的強大能力&#xff0c;僅用樣式就能創造出復雜的圖形&#xff0c;而不需要任何圖片或JavaScript。 項目概述 這個名為"我不是佩奇"的項目是一個純CSS繪制的卡通豬形象…

Spring 中 WebFlux 編寫一個簡單的 Controller

引言&#xff1a;響應式編程與 WebFlux 隨著應用程序需要處理大量并發請求的情況越來越多&#xff0c;傳統的 Servlet 編程模式可能無法滿足高效和低延遲的需求。為了應對這種情況&#xff0c;Spring 5 引入了 WebFlux&#xff0c;一個基于響應式編程的 Web 框架&#xff0c;旨…

React十案例下

代碼下載 登錄模塊 用戶登錄 頁面結構 新建 Login 組件&#xff0c;對應結構: export default function Login() {return (<div className{styles.root}><NavHeader className{styles.header}>賬號登錄</NavHeader><form className{styles.form}>&…

100道C#高頻經典面試題帶解析答案——全面C#知識點總結

100道C#高頻經典面試題帶解析答案 以下是100道C#高頻經典面試題及其詳細解析&#xff0c;涵蓋基礎語法、面向對象編程、集合、異步編程、LINQ等多個方面&#xff0c;旨在幫助初學者和有經驗的開發者全面準備C#相關面試。 &#x1f9d1; 博主簡介&#xff1a;CSDN博客專家、CSD…

機動車號牌管理系統設計與實現(代碼+數據庫+LW)

摘 要 在如今社會上&#xff0c;關于信息上面的處理&#xff0c;沒有任何一個企業或者個人會忽視&#xff0c;如何讓信息急速傳遞&#xff0c;并且歸檔儲存查詢&#xff0c;采用之前的紙張記錄模式已經不符合當前使用要求了。所以&#xff0c;對機動車號牌信息管理的提升&…

VMWare Workstation Pro17.6最新版虛擬機詳細安裝教程(附安裝包教程)

目錄 前言 一、VMWare虛擬機下載 二、VMWare虛擬機安裝 三、運行虛擬機 前言 VMware 是全球領先的虛擬化技術與云計算解決方案提供商&#xff0c;通過軟件模擬計算機硬件環境&#xff0c;允許用戶在一臺物理設備上運行多個獨立的虛擬操作系統或應用。其核心技術可提升硬件…

DeepSeek的神經元革命:穿透搜索引擎算法的下一代內容基建

DeepSeek的神經元革命&#xff1a;穿透搜索引擎算法的下一代內容基建 ——從語義網絡到價值共識的范式重構 一、搜索引擎的“內容饑渴癥”與AI的基建使命 2024年Q1數據顯示&#xff0c;百度索引網頁總數突破3500億&#xff0c;但用戶點擊集中在0.78%的高價值頁面。這種“數據…

docker安裝nginx,基礎命令,目錄結構,配置文件結構

Nginx簡介 Nginx是一款輕量級的Web服務器(動靜分離)/反向代理服務器及電子郵件&#xff08;IMAP/POP3&#xff09;代理服務器。其特點是占有內存少&#xff0c;并發能力強. &#x1f517;官網 docker安裝Nginx &#x1f433; 一、前提條件 ? 已安裝 Docker&#xff08;dock…

Python Lambda表達式詳解

Python Lambda表達式詳解 1. Lambda是什么&#xff1f; Lambda是Python中用于創建匿名函數&#xff08;沒有名字的函數&#xff09;的關鍵字&#xff0c;核心特點是簡潔。它適用于需要臨時定義簡單函數的場景&#xff0c;或直接作為參數傳遞給高階函數&#xff08;如map()、f…

基礎知識補充篇:什么是DAPP前端連接中的provider

專欄:區塊鏈入門到放棄查看目錄-CSDN博客文章瀏覽閱讀352次。為了方便查看將本專欄的所有內容列出目錄,按照順序查看即可。后續也會在此規劃一下后續內容,因此如果遇到不能點擊的,代表還沒有更新。聲明:文中所出觀點大多數源于筆者多年開發經驗所總結,如果你想要知道區塊…

P1115 最大子段和

P1115 最大子段和 - 洛谷 題目描述 給出一個長度為 n 的序列 a&#xff0c;選出其中連續且非空的一段使得這段和最大。 輸入格式 第一行是一個整數&#xff0c;表示序列的長度 n。 第二行有 n 個整數&#xff0c;第 i 個整數表示序列的第 i 個數字 a?。 輸出格式 輸出一…

用實體識別模型提取每一條事實性句子的關鍵詞(實體),并保存到 JSON 文件中

示例代碼&#xff1a; # Generate Keywords import torch import os from tqdm import tqdm import json import nltk import numpy as npfrom span_marker import SpanMarkerModelmodel SpanMarkerModel.from_pretrained("tomaarsen/span-marker-mbert-base-multinerd&…

E8流程多行明細行字符串用I分隔,賦值到主表

需求&#xff1a;明細行摘要字段賦值到主表隱藏字段&#xff0c;隱藏摘要字段在標題中顯示 代碼如下&#xff0c;代碼中的獲取字段名獲取方式&#xff0c;自行轉換成jQuery("#fieldid").val()替換。 //1:參數表單id 2:流程字段名 3:0代表主表&#xff0c;1代表明細…

優化你的 REST Assured 測試:設置默認主機與端口、GET 請求與斷言

REST Assured 是一個功能強大的 Java 庫&#xff0c;用于測試 RESTful Web 服務。它簡化了 API 測試流程&#xff0c;提供了一整套用于高效驗證響應的工具。在本篇博客中&#xff0c;我們將深入探討幾個核心概念&#xff0c;包括如何設置默認主機和端口、如何發起 GET 請求以及…

3.1.3.4 Spring Boot使用使用Listener組件

在Spring Boot中&#xff0c;使用Listener組件可以監聽和響應應用中的各種事件。首先&#xff0c;創建自定義事件類CustomEvent&#xff0c;繼承自ApplicationEvent。然后&#xff0c;創建事件監聽器CustomEventListener&#xff0c;使用EventListener注解標記監聽方法。接下來…

【 vue + js 】引入圖片、base64 編譯顯示圖片

一、引入普通圖片 1、代碼示例&#xff1a; <div class"question"><!-- 錯誤寫法 --><el-empty image"../assets/noinformation.svg" description"暫無問卷"><el-button type"primary">按鈕</el-button&…

JVM 之 String 引用機制解析:常量池、堆內存與 intern 方法

關于常量池中的String類型的數據&#xff0c;在JDK6中只可能是對象&#xff0c;在JDK7中既可以是對象也可以是引用 案例一&#xff1a; String s1 new String("1"); String s2 "1"; System.out.println(s1 s2);s1: 執行 new String("1")&am…

數據庫管理-第313期 分布式挑戰單機,OceanBase單機版試玩(20250411)

數據庫管理313期 2025-04-11 數據庫管理-第313期 分布式挑戰單機&#xff0c;OceanBase單機版試玩&#xff08;20250411&#xff09;1 環境說明2 操作系統配置2.1 關閉防火墻2.2 關閉SELinux2.3 配置hosts文件2.4 配置本地yum源2.5 配置sysctl.conf2.6 配置limits.conf2.7 創建…