news 2026/4/15 17:10:50

飞书扫码登录案例-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/14 21:19:27

Open-AutoGLM赋能AI手机开发(从框架到硬件集成全链路指南)

第一章&#xff1a;Open-AutoGLM开源如何制作ai手机Open-AutoGLM 是一个基于开源大语言模型&#xff08;LLM&#xff09;的自动化框架&#xff0c;旨在为移动设备集成人工智能能力提供轻量化、可定制的解决方案。通过该框架&#xff0c;开发者可以将本地化AI功能深度嵌入到定制…

作者头像 李华
网站建设 2026/4/10 9:35:55

springboot_ssm酒店客房管理系统

目录具体实现截图系统所用技术介绍写作提纲核心代码部分展示系统性能结论源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 springboot_ssm酒店客房管理系统 系统所用技术介绍 本系统采取了一系列的设计原则&#xff0…

作者头像 李华
网站建设 2026/4/11 23:58:54

Keil5 C51环境搭建实战:支持51单片机的配置方法

Keil5 C51环境搭建实战&#xff1a;手把手教你配置51单片机开发环境 你是不是也遇到过这种情况——兴冲冲装好了Keil uVision5&#xff0c;信心满满地新建一个项目&#xff0c;结果一编译就弹出“C51 not found”或“Target not created”&#xff0c; .c 文件语法高亮都乱套…

作者头像 李华
网站建设 2026/4/15 10:13:28

模型推理不再受限,Open-AutoGLM手机端部署实战,轻松实现本地AI运行

第一章&#xff1a;模型推理不再受限&#xff0c;Open-AutoGLM手机端部署实战&#xff0c;轻松实现本地AI运行在移动设备上实现大模型推理不再是遥不可及的技术目标。借助 Open-AutoGLM 框架&#xff0c;开发者可以将轻量化后的 AutoGLM 模型高效部署至安卓手机端&#xff0c;实…

作者头像 李华
网站建设 2026/4/10 17:34:44

初学者必看:HardFault_Handler异常处理入门必看

初学者必看&#xff1a;HardFault_Handler异常处理入门指南你有没有遇到过这样的情况——程序跑着跑着突然“死机”&#xff0c;调试器一接上去&#xff0c;发现它卡在一个叫HardFault_Handler的函数里&#xff1f;代码明明看着没问题&#xff0c;也没有报错提示&#xff0c;却…

作者头像 李华
网站建设 2026/4/14 10:30:03

Scoop终极指南:Windows命令行软件管理革命

Scoop终极指南&#xff1a;Windows命令行软件管理革命 【免费下载链接】Scoop A command-line installer for Windows. 项目地址: https://gitcode.com/gh_mirrors/scoop4/Scoop 还在为Windows软件安装的繁琐流程而烦恼吗&#xff1f;Scoop这款命令行安装工具正在彻底改…

作者头像 李华