news 2026/4/1 17:59:25

飞书扫码登录案例-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/3/30 13:38:28

(Open-AutoGLM部署终极指南):覆盖Ubuntu/CentOS/Windows三大系统

第一章&#xff1a;Open-AutoGLM部署概述Open-AutoGLM 是一个面向自动化自然语言处理任务的开源大模型推理框架&#xff0c;支持多种后端加速引擎与模型量化策略&#xff0c;适用于本地服务器、边缘设备及云原生环境的灵活部署。该框架通过模块化设计实现了模型加载、提示工程、…

作者头像 李华
网站建设 2026/3/26 11:31:28

Emby媒体服务器性能监控工具:实时掌握服务器状态的完整指南

Emby媒体服务器性能监控工具是每个Emby用户必备的强大功能&#xff0c;它能让你实时了解服务器的运行状况&#xff0c;确保媒体服务始终稳定高效。通过内置的Web Dashboard仪表板&#xff0c;你可以轻松监控CPU使用率、内存占用、网络流量等关键指标&#xff0c;及时发现并解决…

作者头像 李华
网站建设 2026/3/29 6:42:22

小天才USB驱动下载实测分享:适配Win10/Win11系统

小天才USB驱动实测指南&#xff1a;让孩子的手表在Win10/Win11上“秒连”你有没有遇到过这种情况——把小天才电话手表插进电脑&#xff0c;结果设备管理器里只显示一个红叉的“未知设备”&#xff1f;孩子急着上传运动轨迹&#xff0c;家长却对着屏幕束手无策。明明线是好的、…

作者头像 李华
网站建设 2026/3/27 4:02:57

ClickHouse性能调优实战:基于TSBS的终极测试指南

ClickHouse性能调优实战&#xff1a;基于TSBS的终极测试指南 【免费下载链接】tsbs Time Series Benchmark Suite, a tool for comparing and evaluating databases for time series data 项目地址: https://gitcode.com/gh_mirrors/ts/tsbs 在时序数据处理领域&#xf…

作者头像 李华
网站建设 2026/3/25 8:19:32

如何在Windows上优雅实现软件自动化管理?

如何在Windows上优雅实现软件自动化管理&#xff1f; 【免费下载链接】Scoop A command-line installer for Windows. 项目地址: https://gitcode.com/gh_mirrors/scoop4/Scoop 还在为每次重装系统后繁琐的软件安装流程而头疼吗&#xff1f;&#x1f914; 作为一名Windo…

作者头像 李华