关于钉钉的三方登录
钉钉三方登录实现指南
目录
- 概述
- 登录方式对比
- 三方登录流程
- 前端实现
- 后端实现
- 注意事项
概述
钉钉提供了两种主要的登录方式:
- 企业内部应用登录(Internal App Login)
- 第三方应用登录(Third-party App Login)
本文档主要介绍第三方应用登录的实现方式,这种方式适用于在浏览器中访问的 Web 应用。
登录方式对比
企业内部应用登录
- 仅支持在钉钉客户端内使用
- 使用
dd.ready()
和dd.runtime.permission.requestAuthCode()
方法 - 需要应用在钉钉工作台可见
- 适合企业内部使用的应用
第三方应用登录
- 支持在浏览器中访问
- 使用扫码登录方式
- 需要独立的登录页面
- 适合面向公众的应用
三方登录流程
- 用户访问应用登录页面
- 前端调用钉钉扫码登录接口
- 用户使用钉钉 App 扫码
- 钉钉服务器回调应用服务器
- 应用服务器获取用户信息
- 完成登录流程
前端实现
1. 引入钉钉 JSAPI
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
2. 实现登录页面
// 登录页面组件
import { useEffect } from 'react';
import { Button, message } from 'antd';
import { useNavigate } from 'react-router-dom';const LoginPage = () => {const navigate = useNavigate();const handleDingTalkLogin = () => {// 钉钉登录配置const config = {id: "login_container", // 容器IDgoto: encodeURIComponent("https://your-app.com/api/dingtalk/callback"), // 回调地址style: "border:none;background-color:#FFFFFF;", // 样式width: "365",height: "400"};// 初始化钉钉登录const handleMessage = (event: MessageEvent) => {const origin = event.origin;if (origin === "https://login.dingtalk.com") {const loginTmpCode = event.data;// 获取到临时码后,调用后端接口handleLogin(loginTmpCode);}};window.addEventListener('message', handleMessage, false);window.DDLogin(config);};const handleLogin = async (loginTmpCode: string) => {try {const response = await fetch('/api/dingtalk/login', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({ loginTmpCode }),});if (response.ok) {const data = await response.json();// 存储用户信息和tokenlocalStorage.setItem('token', data.token);localStorage.setItem('userInfo', JSON.stringify(data.userInfo));message.success('登录成功');navigate('/dashboard');} else {message.error('登录失败');}} catch (error) {message.error('登录失败');}};return (<div className="login-container"><div id="login_container"></div></div>);
};export default LoginPage;
后端实现
1. 处理登录请求
// 登录接口
async function handleDingTalkLogin(req: Request, res: Response) {const { loginTmpCode } = req.body;try {// 1. 获取访问令牌const accessToken = await getAccessToken();// 2. 获取用户信息const userInfo = await getUserInfo(accessToken, loginTmpCode);// 3. 获取用户详细信息const userDetail = await getUserDetail(accessToken, userInfo.userid);// 4. 生成应用tokenconst token = generateToken(userDetail);// 5. 返回用户信息和tokenres.json({token,userInfo: userDetail});} catch (error) {res.status(500).json({ error: '登录失败' });}
}
2. 获取访问令牌
async function getAccessToken() {const appKey = process.env.DINGTALK_APP_KEY;const appSecret = process.env.DINGTALK_APP_SECRET;const response = await fetch(`https://oapi.dingtalk.com/gettoken?appkey=${appKey}&appsecret=${appSecret}`);const data = await response.json();return data.access_token;
}
3. 获取用户信息
async function getUserInfo(accessToken: string, loginTmpCode: string) {const response = await fetch(`https://oapi.dingtalk.com/user/getuserinfo?access_token=${accessToken}&code=${loginTmpCode}`);const data = await response.json();return data;
}
4. 获取用户详细信息
async function getUserDetail(accessToken: string, userId: string) {const response = await fetch(`https://oapi.dingtalk.com/user/get?access_token=${accessToken}&userid=${userId}`);const data = await response.json();return data;
}
注意事项
-
安全性考虑
- 所有敏感信息(如 appKey、appSecret)应存储在环境变量中
- 使用 HTTPS 进行通信
- 实现适当的 token 验证和过期机制
-
错误处理
- 实现完善的错误处理机制
- 提供友好的错误提示
- 记录关键错误日志
-
用户体验
- 提供清晰的登录指引
- 实现登录状态保持
- 优化登录页面加载速度
-
配置要求
- 确保应用已在钉钉开放平台正确配置
- 设置正确的回调域名
- 配置必要的权限范围
-
测试建议
- 在不同浏览器中测试
- 测试各种异常情况
- 验证登录流程的完整性
常见问题
-
Q: 为什么登录失败?
- 检查应用配置是否正确
- 验证回调地址是否配置正确
- 确认网络连接是否正常
-
Q: 如何处理登录超时?
- 实现登录状态检查
- 设置合理的超时时间
- 提供重新登录的选项
-
Q: 如何实现自动登录?
- 使用 refresh token 机制
- 在 token 过期前自动刷新
- 保存必要的登录状态
参考资源
- 钉钉开放平台文档
- 钉钉登录文档
- 钉钉 JSAPI 文档