java后端对接飞书登陆

ops/2025/1/11 14:50:36/

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"

学习笔记仅供学习使用,更详细步骤请移步官方文档。


http://www.ppmy.cn/ops/149183.html

相关文章

30天开发操作系统 第 12 天 -- 定时器 v1.0

前言 定时器(Timer)对于操作系统非常重要。它在原理上却很简单&#xff0c;只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器&#xff0c;CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…

【数据结构】树的定义

在计算机科学中&#xff0c;树&#xff08;Tree&#xff09;是一种重要的基础数据结构&#xff0c;广泛应用于许多领域&#xff0c;如文件系统的目录结构、数据库的索引、编译器的语法树、人工智能的决策树等。理解树的基本概念和术语&#xff0c;对于学习计算机科学及其相关技…

排序:插入、选择、交换、归并排序

排序 &#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性 &#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&#xff0c;…

51c~Pytorch~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/12311033 一、Pytorch~训练-使用 这里介绍了Pytorch中已经训练好的模型如何使用 Pytorch中提供了很多已经在ImageNet数据集上训练好的模型了&#xff0c;可以直接被加载到模型中进行预测任务。预训练模型存放在Pytorch的…

年度技术突破奖|中兴微电子引领汽车芯片新变革

随着以中央计算区域控制为代表的新一代整车电子架构逐步成为行业主流&#xff0c;车企在电动化与智能化之后&#xff0c;正迎来以架构创新为核心的新一轮技术竞争。中央计算SoC&#xff0c;作为支撑智驾和智舱高算力需求的核心组件&#xff0c;已成为汽车电子市场的重要新增量。…

【MySQL】ON与WHERE的区别(临时表)

先说区别后举例 核心概念&#xff1a;临时表 “临时表”&#xff0c;这是理解 JOIN 操作的关键。数据库在执行 JOIN 时&#xff0c;确实会生成一个中间的、逻辑上的结果集&#xff08;你可以把它想象成一个临时表&#xff09;&#xff0c;然后在这个结果集上进行后续操作。性能…

蓝笔科技 | 超凡妈妈赋能计划-【北大生涯规划师特别企划】

12月27日&#xff0c;“超凡妈妈赋能计划-北大生涯规划师特别企划”在广州正式启动&#xff0c;据了解&#xff0c;本次超凡妈妈赋能计划是由广州蓝笔科技信息有限公司牵头发起并主办&#xff0c;中国关心下一代健康体育基金会作为公益支持单位&#xff0c;北京大学作为项目技术…

【Docker】Dockerfile ENV环境变量传递问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、直接说结论二、怎么解决这个问题方案1. 不使用多级嵌套的变量方案2. 使用 bash 在启动时动态解析环境变量 前言 这两天在给生产环境部署skywalking链路监控…