java后端对接飞书登陆
java后端对接飞书登陆

项目要求对接第三方登陆,飞书登陆,次笔记仅针对java后端,在看本笔记前,默认已在飞书开发方已建立了应用,并获取到了appid和appsecret。后端要做的其实很简单,基本都是前端做的,后端要做的就是接收前端请求飞书获取到一个code后传到后端,后端拿到code后请求登陆人在飞书的token信息,同时根据token请求飞书接口获取登陆人信息返回给前端就好了,官网开发指导有很详细的教程,可以看下官方文档。
飞书官方文档
废话不多书,直接上代码:
首先controller:
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;/*** @author xx*/
@RestController
@Api(tags = "飞书登陆认证")
public class FeiShuController {@Resourceprivate FeiShuService feiShuService;@GetMapping("/getUserByCode/{code}")@ApiOperation(value = "根据code获取飞书用户信息")public Response<FeishuUserResp> login(@PathVariable String code) {//获取飞书用户tokenFeiShuTokenResp feiShuTokenVo = feiShuService.getTokenByCode(code);//根据token获取用户信息FeishuUserResp userInfo = feiShuService.getUserInfoByToken(feiShuTokenVo);// 刷新tokenreturn Response.ok(userInfo);}@GetMapping("/refreshToken/{refreshToken}")@ApiModelProperty(value = "刷新token")public Response<FeishuUserResp> refreshToken(@PathVariable String refreshToken){return Response.ok(feiShuService.refreshToken(refreshToken));}@PostMapping("/login-out/{userId}")@ApiOperation(value = "飞书退出登陆")public Response loginOut(HttpServletRequest request, @PathVariable String userId){return feiShuService.loginOut(request,userId);}
}
service层:
import cn.hutool.http.Header;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;@Slf4j
@Service
public class FeiShuServiceImpl implements FeiShuService {// 应用appid@Value("${feishu.app.id}")private String appid;// 应用appsecret@Value("${feishu.app.secret}")private String appsecret;// 飞书获取token地址@Value("${feishu.userAccessTokenUrl}")private String userAccessTokenUrl;// 回调地址(需前端提供)@Value("${feishu.redirectUrl}")private String redirectUrl;// 获取用户详情@Value("${feishu.userInfoUrl}")private String userInfoUrl;// 登出飞书地址@Value("${feishu.logOutUrl}")private String loginOutUrl;//刷新飞书token地址@Value("${feishu.refreshTokenUrl}")private String refreshTokenUrl;@Resourceprivate RedisService redisService;/*** 根据code获取token** @param code code* @return token*/@Overridepublic FeiShuTokenResp getTokenByCode(String code) {Map<String, Object> params = new HashMap<>(4);params.put("grant_type", "authorization_code");params.put("client_id", appid);params.put("client_secret", appsecret);params.put("code", code);params.put("redirect_uri", redirectUrl);String result = HttpUtil.createPost(userAccessTokenUrl).header(Header.CONTENT_TYPE, "application/json").charset("utf-8").body(JSON.toJSONString(params)).execute().body();if (StringUtils.isEmpty(result)) {throw new RuntimeException("获取飞书token失败!");}JSONObject parseObj = JSONUtil.parseObj(result);if ((int) parseObj.get("code") != 0) {throw new RuntimeException("请求飞书token失败:" + parseObj.get("error_description"));}//tokenString userAccessToken = (String) parseObj.get("access_token");//token过期时间long expiresIn = (int) parseObj.get("expires_in");// refresh_tokenString refreshToken = (String) parseObj.get("refresh_token");// 刷新token的过期时间long refreshTokenExpiresIn = (int) parseObj.get("refresh_token_expires_in");// token_typeString tokenType = (String) parseObj.get("token_type");return FeiShuTokenResp.builder().tokenType(tokenType).accessToken(userAccessToken).expiresIn(expiresIn).refreshToken(refreshToken).refreshTokenExpiresIn(refreshTokenExpiresIn).build();}/*** 获取用户详情** @return 用户信息*/@Overridepublic FeishuUserResp getUserInfoByToken(FeiShuTokenResp feiShuTokenVo) {String token = feiShuTokenVo.getTokenType() + " " + feiShuTokenVo.getAccessToken();String result = HttpUtil.createGet(userInfoUrl).header("Authorization", token).header(Header.CONTENT_TYPE, "application/json").charset("utf-8").execute().body();if (StringUtils.isEmpty(result)) {throw new RuntimeException("请求飞书用户详情接口失败!");}JSONObject parseObj = JSONUtil.parseObj(result);if ((int) parseObj.get("code") != 0) {throw new RuntimeException("获取飞书用户信息失败:" + parseObj.get("msg"));}Object data = parseObj.get("data");FeishuUserResp userResp = JSONUtil.toBean(JSONUtil.toJsonStr(data), FeishuUserResp.class);userResp.setAccessToken(feiShuTokenVo.getAccessToken());userResp.setRefreshToken(feiShuTokenVo.getRefreshToken());// 缓存tokenString tokenKey = GlobalConstant.TOKEN + ":" + feiShuTokenVo.getAccessToken();redisService.setCacheObject(tokenKey, feiShuTokenVo.getAccessToken(), feiShuTokenVo.getExpiresIn(), TimeUnit.SECONDS);//缓存用户信息redisService.setCacheObject(feiShuTokenVo.getAccessToken(), userResp, feiShuTokenVo.getExpiresIn(), TimeUnit.SECONDS);//缓存刷新tokenif (Objects.nonNull(feiShuTokenVo.getRefreshToken())) {String refreshTokenKey = GlobalConstant.REFRESH_TOKEN + ":" + feiShuTokenVo.getRefreshToken();redisService.setCacheObject(refreshTokenKey, feiShuTokenVo.getRefreshToken(), feiShuTokenVo.getRefreshTokenExpiresIn(), TimeUnit.SECONDS);//缓存redisService.setCacheObject(feiShuTokenVo.getRefreshToken(), userResp, feiShuTokenVo.getRefreshTokenExpiresIn(), TimeUnit.SECONDS);}return userResp;}/*** 飞书退出登陆** @param userId user_id* @return 返回*/@Overridepublic Response loginOut(HttpServletRequest request, String userId) {String token = request.getHeader(GlobalConstant.TOKEN);Map<String, Object> params = new HashMap<>(2);params.put("logout_type", 1);params.put("user_id", userId);String result = HttpUtil.createPost(loginOutUrl).header("Authorization", "").header(Header.CONTENT_TYPE, "application/json").charset("utf-8").body(JSONUtil.toJsonStr(params)).execute().body();if (result == null) {throw new RuntimeException("飞书退出登陆失败!");}JSONObject parseObj = JSONUtil.parseObj(result);//删除缓存tokenFeishuUserResp object = redisService.getCacheObject(token);redisService.deleteObject(object.getAccessToken());redisService.deleteObject(object.getRefreshToken());redisService.deleteObject(GlobalConstant.TOKEN + ":" + token);redisService.deleteObject(GlobalConstant.REFRESH_TOKEN + ":" + object.getRefreshToken());if ((int) parseObj.get("code") == 0) {return Response.ok();}return Response.failed(parseObj.get("msg").toString());}/*** 刷新token** @param refreshToken 刷新token* @return 返回*/@Overridepublic FeishuUserResp refreshToken(String refreshToken) {Map<String, String> params = new HashMap<>(4);params.put("grant_type", "refresh_token");params.put("client_id", appid);params.put("client_secret", appsecret);params.put("refresh_token", refreshToken);String result = HttpUtil.createPost(refreshTokenUrl).header(Header.CONTENT_TYPE, "application/json").charset("utf-8").body(JSONUtil.toJsonStr(params)).execute().body();if (Objects.isNull(result)) {throw new RuntimeException("刷新token失败!");}JSONObject parseObj = JSONUtil.parseObj(result);if (20037 == (int) parseObj.get("code")) {throw new RuntimeException("refresh_token已过期");}if (0 != (int) parseObj.get("code")) {throw new RuntimeException("请求飞书刷新token接口失败:" + parseObj.get("error_description"));};//tokenString token = (String) parseObj.get("access_token");//token过期时间long expiresIn = (long) parseObj.get("expires_in");//刷新tokenString refreshToken1 = (String) parseObj.get("refresh_token");//刷新token 过期时间long refreshTokenExpiresIn = (long) parseObj.get("refresh_token_expires_in");FeishuUserResp resp = redisService.getCacheObject(refreshToken);resp.setAccessToken(token);resp.setRefreshToken(refreshToken1);//缓存tokenredisService.setCacheObject(GlobalConstant.TOKEN + ":" + token, token, expiresIn, TimeUnit.MINUTES);redisService.setCacheObject(token, resp, expiresIn, TimeUnit.MINUTES);Object object = redisService.getCacheObject(GlobalConstant.REFRESH_TOKEN + ":" + refreshToken1);if (Objects.isNull(object)) {redisService.setCacheObject(GlobalConstant.REFRESH_TOKEN + ":" + refreshToken1, refreshToken1, refreshTokenExpiresIn, TimeUnit.MINUTES);redisService.setCacheObject(refreshToken1, resp, refreshTokenExpiresIn, TimeUnit.MINUTES);}return resp;}
}
yml文件飞书相关配置
# 飞书相关配置
feishu:app:id: "xxxxxx"secret: "xxxxxx"# 获取token地址userAccessTokenUrl: "https://open.feishu.cn/open-apis/authen/v2/oauth/token"# 回调地址(可更改)redirectUrl: "http://xxxxxx/upgrade/login"# 获取用户信息地址userInfoUrl: "https://open.feishu.cn/open-apis/authen/v1/user_info"# 登出logOutUrl: "https://open.feishu.cn/open-apis/passport/v1/sessions/logout"# 刷新token地址refreshTokenUrl: "https://open.feishu.cn/open-apis/authen/v2/oauth/token"
学习笔记仅供学习使用,更详细步骤请移步官方文档。
相关文章:
java后端对接飞书登陆
java后端对接飞书登陆 项目要求对接第三方登陆,飞书登陆,次笔记仅针对java后端,在看本笔记前,默认已在飞书开发方已建立了应用,并获取到了appid和appsecret。后端要做的其实很简单,基本都是前端做的&…...
记录一次Android Studio的下载、安装、配置
目录 一、下载和安装 Android Studio 1、搜索下载Android studio 2、下载成功后点击安装包进行安装: 3、这里不用打勾,直接点击安装 : 4、完成安装: 5、这里点击Cancel就可以了 6、接下来 7、点击自定义安装:…...
直流无刷电机控制(FOC):电流模式
目录 概述 1 系统框架结构 1.1 硬件模块介绍 1.2 硬件实物图 1.3 引脚接口定义 2 代码实现 2.1 软件架构 2.2 电流检测函数 3 电流环功能实现 3.1 代码实现 3.2 测试代码实现 4 测试 概述 本文主要介绍基于DengFOC的库函数,实现直流无刷电机控制&#x…...
73.矩阵置零 python
矩阵置零 题目题目描述示例 1:示例 2:提示: 题解思路分析Python 实现代码代码解释提交结果 题目 题目描述 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例…...
垃圾收集算法
分代收集理论 分代收集理论,建立在两个分代假说之上。 弱分代假说:绝大多数对象都是朝圣夕灭的。 强分代假说:熬过越多次垃圾收集的过程的对象就越难以消亡。 这两个分代假说奠定了垃圾收集器的一致设计原则:收集器应该将Java…...
SQL-leetcode-262. 行程和用户
262. 行程和用户 表:Trips --------------------- | Column Name | Type | --------------------- | id | int | | client_id | int | | driver_id | int | | city_id | int | | status | enum | | request_at | varchar | --------------------- id 是这张表的主键…...
太原理工大学软件设计与体系结构 --javaEE
这个是简答题的内容 选择题的一些老师会给你们题库,一些注意的点我会做出文档在这个网址 项目目录预览 - TYUT复习资料:复习资料 - GitCode 希望大家可以给我一些打赏 什么是Spring的IOC和DI IOC 是一种设计思想,它将对象的创建和对象之间的依赖关系…...
Leetcode 139. 单词拆分 动态规划
原题链接:Leetcode 139. 单词拆分 递归,超时 class Solution { public:bool isfind(string s,map<string,int>& mp){for(auto x:mp){string wordx.first;if(sword) return true;int nword.size();if(n>s.size()) continue;string s1s.subs…...
python异常机制
异常是什么? 软件程序在运行过程中,非常可能遇到刚刚提到的这些问题,我们称之为异常,英文是Exception,意思是例外。遇到这些例外情况,或者交异常,我们怎么让写的程序做出合理的处理,…...
运行爬虫时可能遇到哪些常见问题?
在运行Python爬虫时,可能会遇到以下一些常见问题及相应的解决方法: 1. 请求频繁被封 IP 问题描述:爬虫请求频繁时,网站可能会识别到异常行为并封禁 IP,从而导致后续请求失败。解决方法: 使用代理…...
BGP与CN2的区别 详解两者在网络传输中的应用与优势
在现代互联网环境中,选择合适的网络传输协议和解决方案对于企业的业务运行至关重要。BGP(Border Gateway Protocol)和CN2(China Telecom Next Carrier Network)是两种广泛应用的网络技术,但它们的设计理念、…...
Spring 项目 基于 Tomcat容器进行部署
文章目录 一、前置知识二、项目部署1. 将写好的 Spring 项目先打包成 war 包2. 查看项目工件(Artifact)是否存在3. 配置 Tomcat3.1 添加一个本地 Tomcat 容器3.2 将项目部署到 Tomcat 4. 运行项目 尽管市场上许多新项目都已经转向 Spring Boot࿰…...
“负载均衡”出站的功能、原理与场景案例
在企业日常网络中,外网访问速度不稳定是一个常见问题。特别是多条外网线路并行时,不合理的流量分配会导致资源浪费甚至网络拥堵。而出站负载均衡,正是解决这一问题的关键技术。 作为一种先进的网络流量管理技术,其核心是优化企业内…...
02-51单片机数码管与矩阵键盘
一、数码管模块 1.数码管介绍 如图所示为一个数码管的结构图: 说明: 数码管上下各有五个引脚,其中上下中间的两个引脚是联通的,一般为数码管的公共端,分为共阴极或共阳极;其它八个引脚分别对应八个二极管…...
不同方式获取音频时长 - python 实现
DataBall 助力快速掌握数据集的信息和使用方式,会员享有 百种数据集,持续增加中。 需要更多数据资源和技术解决方案,知识星球: “DataBall - X 数据球(free)” -------------------------------------------------------------…...
【python A* pygame 格式化 自定义起点、终点、障碍】
- pip install pygame test.py(chatgpt版本) 空格键:运行 A* 算法。CtrlC 键:清空路径。CtrlS 键:保存当前地图到 map.json 文件。CtrlL 键:从 map.json 文件加载地图。 import pygame import json from queue import PriorityQ…...
12_Redis发布订阅
1.Redis发布订阅介绍 1.1 基本概念 Redis的发布订阅(Pub/Sub)是一种消息通信模式,允许消息的发布者(Publisher)将消息发布到一个或多个频道(Channel),订阅者(Subscriber)通过订阅这些频道来接收消息。 发布者(Publisher):发送消息的一方,使用PUBLISH命令将消息…...
归并排序:数据排序的高效之道
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
【redis初阶】浅谈分布式系统
目录 一、常见概念 1.1 基本概念 2.2 评价指标(Metric) 二、架构演进 2.1 单机架构 2.2 应用数据分离架构 2.3 应用服务集群架构 2.4 读写分离/主从分离架构 2.5 引入缓存 ⸺ 冷热分离架构 2.6 数据库分库分表 2.7 业务拆分 ⸺ 引入微服务 redis学习&…...
CatLog的使用
一 CatLog的简介 1.1 作用 CAT(Central Application Tracking) 是基于 Java 开发的实时应用监控平台,为美团点评提供了全面的实时监控告警服务。 1.2 组成部分 1.2.1 Transaction 1.Transaction 适合记录跨越系统边界的程序访问行为&a…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
