[Python] 企业内部应用接入钉钉登录,端内免登录+浏览器授权登录

Python\] 为企业网站应用接入钉钉鉴权,实现钉钉客户端内自动免登授权,浏览器中手动钉钉授权登录两种逻辑。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b961e68f00e94184b95f60bd4293edd6.png) #### 操作步骤 1. 企业内部获得 `开发者权限`,没有的话先申请。 2. 访问 [钉钉开放平台-应用开发](https://open-dev.dingtalk.com/fe/app) 创建一个 `企业内部应用-钉钉应用`。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/df1d23ce63eb47d1a284cdcb5dcb3918.png) 3. 打开应用详情页,获取 **`Client ID`、`Client Secret`、`CorpId`** 备用,获取方式如下图所示。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c88c6431c536476580192303ef66c0fc.png) 4. 编写代码,搭建相应服务(见下方示例代码) *** ** * ** *** 示例代码(以`Flask`作为后端): ##### - `templates/auth.html` ```html auth ``` * 需修改:`corpId: "dingxxxxxxxxxx"` 替换为真实的`CorpId` * 代码逻辑:*若在钉钉端内,则借助钉钉免登码完成登录。反之,则跳转钉钉授权页面进行授权登录(授权页面重定向由后端控制,当然直接写在前端也可以)* ##### - `app.py` ```python # -*- 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_api app = 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']) # 你好,{{user.name}} 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 ID return 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() # 返回具体的错误内容和完整的 traceback response = result_map(500, False, str(error), traceback_info) return jsonify(response), 500 if __name__ == '__main__': app.run(host="0.0.0.0", port=5000) ``` * 需修改:`client_id = "dingyyyyyyyyyy"` 替换为真实的`Client ID` ##### - `utils/dingtalk_api.py` ```python # -*- coding: utf-8 -*- # Author: 薄荷你玩 # Date: 2025/04/07 import requests DINGTALK_DOMAIN = "https://api.dingtalk.com" CorpId = "dingxxxxxxxxxx" # 企业ID ClientId = "dingyyyyyyyyyy" # Client ID ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Client Secret def 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 res def 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 res def 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 res def 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 res def 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 res def 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) ``` * 需修改: ```python CorpId = "dingxxxxxxxxxx" # 企业ID ClientId = "dingyyyyyyyyyy" # Client ID ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Client Secret ``` 替换为真实的ID或秘钥 *** ** * ** *** 5. 配置回调域名,如下图所示,填写用户授权后的回调地址(如:http://192.168.2.1:5000/demo/oauth-web),实际使用中换成正式的服务域名。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/640ce48397194d80bae2f77f830f5b55.png) 6. 配置完成后,钉钉内访问 `/demo` (如:http://192.168.2.1:5000/demo)即可自动登录(获取姓名和头像等信息);浏览器访问会自动跳转钉钉授权登录页面,授权后完成登录。

相关推荐
Aision_3 分钟前
从工具调用到 MCP、Skill完整学习记录
java·python·gpt·学习·langchain·prompt·agi
辞旧 lekkk4 小时前
【Qt】信号和槽
linux·开发语言·数据库·qt·学习·mysql·萌新
2zcode5 小时前
运动模糊图像复原的MATLAB仿真与优化
开发语言·matlab
袁雅倩19975 小时前
当吸尘器、筋膜枪都用上Type-C,供电方案该怎么选?浅谈PD取电芯片ECP5702的应用
c语言·开发语言·支持向量机·动态规划·推荐算法·最小二乘法·图搜索算法
2301_809204705 小时前
JavaScript中严格模式use-strict对引擎解析的辅助.txt
jvm·数据库·python
zjy277776 小时前
mysql如何选择合适的索引类型_mysql索引设计实战
jvm·数据库·python
Aaswk6 小时前
Java Lambda 表达式与流处理
java·开发语言·python
万邦科技Lafite6 小时前
京东item_get接口实战案例:实时商品价格监控全流程解析
java·开发语言·数据库·python·开放api·淘宝开放平台
Cyber4K7 小时前
【Python专项】进阶语法-系统资源监控与数据采集(1)
开发语言·python·php
Le_ee8 小时前
ctfweb:php/php短标签/.haccess+图片马/XXE
开发语言·前端·php