news 2026/4/28 16:36:03

飞书扫码登录案例-springboot版

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
飞书扫码登录案例-springboot版

飞书扫码登录案例-springboot版

思路:飞书开发者后台注册应用,填写id、密钥即可;搭配公网映射/内网穿透,懂的都懂;飞书和钉钉比较相似,配个安全设置的扫码回调即可

一、controller

import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/lark") public class LarkLoginController { private final String appId = "cli_xxxxxee"; // 填你的 App ID private final String appSecret = "RQtq8xxxxxHXpABqjm"; // 填你的 App Secret @GetMapping("/callback") public String callback(@RequestParam("code") String code) { // 1. 获取 app_access_token (这是飞书特有的,先拿应用的 Token) // 注意:实际开发中 app_access_token 应该缓存 2 小时,不要每次都请求 String appTokenUrl = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal"; Map<String, String> appTokenParams = new HashMap<>(); appTokenParams.put("app_id", appId); appTokenParams.put("app_secret", appSecret); RestTemplate restTemplate = new RestTemplate(); Map appTokenResp = restTemplate.postForObject(appTokenUrl, appTokenParams, Map.class); String appAccessToken = (String) appTokenResp.get("app_access_token"); // 2. 用 code 换取用户的 user_access_token String userTokenUrl = "https://open.feishu.cn/open-apis/authen/v1/oidc/access_token"; Map<String, Object> body = new HashMap<>(); body.put("grant_type", "authorization_code"); body.put("code", code); org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders(); headers.add("Authorization", "Bearer " + appAccessToken); headers.add("Content-Type", "application/json; charset=utf-8"); org.springframework.http.HttpEntity<Map> request = new org.springframework.http.HttpEntity<>(body, headers); Map userTokenResp = restTemplate.postForObject(userTokenUrl, request, Map.class); // 【修复点1】将第一个 data 变量重命名为 tokenData Map tokenData = (Map) userTokenResp.get("data"); if (tokenData == null) return "登录失败: " + userTokenResp; String userAccessToken = (String) tokenData.get("access_token"); // 3. 获取用户信息 String userInfoUrl = "https://open.feishu.cn/open-apis/authen/v1/user_info"; org.springframework.http.HttpHeaders infoHeaders = new org.springframework.http.HttpHeaders(); infoHeaders.add("Authorization", "Bearer " + userAccessToken); org.springframework.http.HttpEntity<String> infoRequest = new org.springframework.http.HttpEntity<>(null, infoHeaders); Map userInfoResp = restTemplate.exchange(userInfoUrl, org.springframework.http.HttpMethod.GET, infoRequest, Map.class).getBody(); // 检查是否成功 if (userInfoResp == null || !Integer.valueOf(0).equals(userInfoResp.get("code"))) { return "<h3>获取用户信息失败</h3><p>" + userInfoResp + "</p>"; } // 【修复点2】将第二个 data 变量重命名为 userData,避免冲突 Map userData = (Map) userInfoResp.get("data"); String name = (String) userData.get("name"); // 姓名 String enName = (String) userData.get("en_name"); // 英文名 String avatarUrl = (String) userData.get("avatar_big"); // 头像(大图) String userId = (String) userData.get("user_id"); // 用户ID String unionId = (String) userData.get("union_id"); // 统一ID // 3. 拼装 HTML 卡片 StringBuilder html = new StringBuilder(); html.append("<!DOCTYPE html>"); html.append("<html><head><meta charset='utf-8'><title>登录成功</title>"); html.append("<style>"); html.append("body { font-family: sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f2f5; }"); html.append(".card { background: white; padding: 40px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); text-align: center; width: 300px; }"); html.append(".avatar { width: 100px; height: 100px; border-radius: 50%; border: 4px solid #3370ff; margin-bottom: 20px; }"); html.append("h2 { color: #1f2329; margin: 10px 0; }"); html.append("p { color: #646a73; font-size: 14px; margin: 5px 0; }"); html.append(".tag { display: inline-block; background: #e1eaff; color: #3370ff; padding: 4px 8px; border-radius: 4px; font-size: 12px; margin-top: 15px; }"); html.append("</style></head>"); html.append("<body>"); html.append("<div class='card'>"); html.append("<img class='avatar' src='" + avatarUrl + "' />"); html.append("<h2>" + name + "</h2>"); if (enName != null && !enName.isEmpty()) { html.append("<p>" + enName + "</p>"); } html.append("<div class='tag'>登录成功</div>"); html.append("<hr style='margin: 20px 0; border: none; border-top: 1px solid #eee;'/>"); html.append("<p style='text-align:left'><strong>User ID:</strong> " + userId + "</p>"); html.append("<p style='text-align:left'><strong>Union ID:</strong> " + unionId + "</p>"); html.append("</div>"); html.append("</body></html>"); return html.toString(); }}

二、html页面

<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>飞书扫码登录</title> <script src="https://lf-package-cn.feishucdn.com/obj/feishu-static/lark/passport/qrcode/LarkSSOSDKWebQRCode-1.0.3.js"></script> </head> <body> <h2 style="text-align: center;">请使用飞书 App 扫码登录</h2> <div id="lark_login_container" style="display: flex; justify-content: center; margin-top: 50px;"></div> <script> window.onload = function() { if (typeof QRLogin === 'undefined') { alert("SDK 加载失败,请检查网络!"); return; } var appId = "cli_a9xxxxx5cee"; // ⚠️ 注意:这里必须和您浏览器地址栏的域名保持一致! // 如果您访问的是 localhost:8080,这里也要填 localhost // 如果您访问的是 xxxxx:28088,这里也要填 xxxxx var redirectUri = "http://yxxxxx:28088/lark/callback"; // 1. 【核心修复】先定义好跳转地址变量 gotoUrl var gotoUrl = "https://passport.feishu.cn/suite/passport/oauth/authorize?client_id=" + appId + "&redirect_uri=" + encodeURIComponent(redirectUri) + "&response_type=code" + "&state=STATE_TEST"; var QRLoginObj = QRLogin({ id: "lark_login_container", goto: gotoUrl, // 这里直接使用变量 width: "300", height: "300", style: "width:300px;height:300px" }); var handleMessage = function (event) { // 飞书验证源 if (QRLoginObj.matchOrigin(event.origin) && QRLoginObj.matchData(event.data)) { var loginTmpCode = event.data.tmp_code; // 2. 【核心修复】直接使用 gotoUrl 变量,而不是 QRLoginObj.goto (它是 undefined) // 逻辑:拿着临时 code 跳去飞书认证页,飞书认证完会自动跳回你的 redirectUri window.location.href = `${gotoUrl}&tmp_code=${loginTmpCode}`; } }; if (typeof window.addEventListener != 'undefined') { window.addEventListener('message', handleMessage, false); } else if (typeof window.attachEvent != 'undefined') { window.attachEvent('onmessage', handleMessage); } console.log("飞书扫码组件初始化成功"); }; </script> </body> </html>

三、访问页面

飞书扫码即可;

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 17:15:57

【Open-AutoGLM安装避坑手册】:90%新手都会忽略的7个细节

第一章&#xff1a;Open-AutoGLM部署安装概述Open-AutoGLM 是一个面向自动化自然语言生成任务的开源框架&#xff0c;支持模型快速部署、推理优化与任务编排。其设计目标是简化大语言模型在实际生产环境中的集成流程&#xff0c;提供模块化组件以支持灵活扩展。本章介绍其核心部…

作者头像 李华
网站建设 2026/4/25 0:49:17

OpenWrt路由器完整改造手册:快速免费升级旧设备

还在为家里老旧路由器的有限功能而烦恼吗&#xff1f;想要实现更强大的网络管理能力却不知从何入手&#xff1f;通过OpenWrt系统改造&#xff0c;你完全可以免费让这些"退役"设备焕发新生&#xff0c;获得比市面新款路由器更丰富的功能特性。本文将为你提供一套完整的…

作者头像 李华
网站建设 2026/4/26 0:33:23

终极指南|如何用开源工具重构你的笔记工作流

终极指南&#xff5c;如何用开源工具重构你的笔记工作流 【免费下载链接】open-notebook An Open Source implementation of Notebook LM with more flexibility and features 项目地址: https://gitcode.com/GitHub_Trending/op/open-notebook 你是否曾经遇到过这样的困…

作者头像 李华
网站建设 2026/4/25 19:28:29

YOLO模型训练任务支持定时启动吗?GPU资源预约功能上线

YOLO模型训练任务支持定时启动吗&#xff1f;GPU资源预约功能上线 在现代AI研发团队中&#xff0c;一个再熟悉不过的场景是&#xff1a;工程师深夜守在电脑前&#xff0c;反复刷新GPU监控页面&#xff0c;只为抢到一张空闲显卡来启动一次长达十几个小时的YOLO模型训练。这种“人…

作者头像 李华
网站建设 2026/4/28 17:52:06

OrcaSlicer终极指南:5大功能解析助你快速上手3D打印

OrcaSlicer终极指南&#xff1a;5大功能解析助你快速上手3D打印 【免费下载链接】OrcaSlicer G-code generator for 3D printers (Bambu, Prusa, Voron, VzBot, RatRig, Creality, etc.) 项目地址: https://gitcode.com/GitHub_Trending/orc/OrcaSlicer 想要从3D打印小白…

作者头像 李华
网站建设 2026/4/24 20:31:58

腾讯混元3D-Part:智能建模革命的技术深度解析与实战应用

腾讯混元3D-Part&#xff1a;智能建模革命的技术深度解析与实战应用 【免费下载链接】Hunyuan3D-Part 腾讯混元3D-Part 项目地址: https://ai.gitcode.com/tencent_hunyuan/Hunyuan3D-Part 想象一下&#xff0c;当传统3D建模师还在为复杂模型的手动分割而熬夜加班时&…

作者头像 李华