java后端对接飞书登陆

news/2025/1/11 21:35:04 标签: 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 {

    @Resource
    private FeiShuService feiShuService;

    @GetMapping("/getUserByCode/{code}")
    @ApiOperation(value = "根据code获取飞书用户信息")
    public Response<FeishuUserResp> login(@PathVariable String code) {
        //获取飞书用户token
        FeiShuTokenResp feiShuTokenVo = feiShuService.getTokenByCode(code);
        //根据token获取用户信息
        FeishuUserResp userInfo = feiShuService.getUserInfoByToken(feiShuTokenVo);
        // 刷新token
        return 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;

    @Resource
    private RedisService redisService;

    /**
     * 根据code获取token
     *
     * @param code code
     * @return token
     */
    @Override
    public 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"));
        }
        //token
        String userAccessToken = (String) parseObj.get("access_token");
        //token过期时间
        long expiresIn = (int) parseObj.get("expires_in");
        // refresh_token
        String refreshToken = (String) parseObj.get("refresh_token");
        // 刷新token的过期时间
        long refreshTokenExpiresIn = (int) parseObj.get("refresh_token_expires_in");
        // token_type
        String tokenType = (String) parseObj.get("token_type");

        return FeiShuTokenResp.builder()
                .tokenType(tokenType)
                .accessToken(userAccessToken)
                .expiresIn(expiresIn)
                .refreshToken(refreshToken)
                .refreshTokenExpiresIn(refreshTokenExpiresIn)
                .build();
    }

    /**
     * 获取用户详情
     *
     * @return 用户信息
     */
    @Override
    public 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());

        // 缓存token
        String tokenKey = GlobalConstant.TOKEN + ":" + feiShuTokenVo.getAccessToken();
        redisService.setCacheObject(tokenKey, feiShuTokenVo.getAccessToken(), feiShuTokenVo.getExpiresIn(), TimeUnit.SECONDS);
        //缓存用户信息
        redisService.setCacheObject(feiShuTokenVo.getAccessToken(), userResp, feiShuTokenVo.getExpiresIn(), TimeUnit.SECONDS);
        //缓存刷新token
        if (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 返回
     */
    @Override
    public 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);

        //删除缓存token
        FeishuUserResp 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 返回
     */
    @Override
    public 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"));
        }
        ;
        //token
        String token = (String) parseObj.get("access_token");
        //token过期时间
        long expiresIn = (long) parseObj.get("expires_in");
        //刷新token
        String 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);
        //缓存token
        redisService.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.niftyadmin.cn/n/5820228.html

相关文章

[Unity]发包前遇到的坑之GridLayoutGroup

发包前禁用了UI上面一个调试页面A后&#xff0c;发现无法正确获取某一个用了GridLayoutGroup组件的所有子物体的世界坐标。 一顿研究之后发现&#xff0c;在Start的时候想要正确获取其坐标&#xff0c;需要强制刷新一次布局&#xff0c;方法如下&#xff1a; UnityEngine.U…

ffmpeg7.0 aac转pcm

#pragma once #define __STDC_CONSTANT_MACROS #define _CRT_SECURE_NO_WARNINGSextern "C" { #include "libavcodec/avcodec.h" }//缓冲区大小&#xff08;缓存5帧数据&#xff09; #define AUDIO_INBUF_SIZE 40960 /*name depthu8 8s16 …

Euler 21.10安装oracle 19.22单机安装

1.虚拟机安装 服务器的名称和hosts文件名称要一样 hostnamectl set-hostname hfdb95 && bash 查看系统版本&#xff1a; uname -a cat /etc/redhat-release 2.IP地址配置 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOnone DEFROUTEyes IPV4_FAILURE_…

ffmpeg 常用命令 案例

更详细请参考ffmpeg手册&#xff0c;下载ffmpegrelease版后在doc中就有&#xff0c;主页面。 ffmpeg主要命令的详细参考&#xff1a; ffmpeg Documentation # Main-options -version -formats -demuxers -protocols -muxers -filters -devices -codecs -sample_fmts -decode…

w~自动驾驶~合集16

我自己的原文哦~ https://blog.51cto.com/whaosoft/12765612 #SIMPL 用于自动驾驶的简单高效的多智能体运动预测基准 原标题&#xff1a;SIMPL: A Simple and Efficient Multi-agent Motion Prediction Baseline for Autonomous Driving 论文链接&#xff1a;https://ar…

mysql message from server: “Too many connections“

背景 新部署完的数据库&#xff0c;一些基本参数未设置时&#xff0c;应用启动后&#xff0c;日志报错如下 2025-01-09 09:54:40.162 [TID: N/A] [main] ERROR c.a.m.studio.runner.ExtraDDLRunner -加载应用动态数据源失败 com.zaxxer.hikari.pool.HikariPool$PoolInitializ…

如何使用Scala和Selenium爬取知乎视频并保存到本地

一、环境准备 在开始之前&#xff0c;我们需要确保已经安装了以下环境和工具&#xff1a; Java开发环境&#xff1a;Selenium是基于Java开发的&#xff0c;因此需要先安装Java开发环境&#xff0c;可以从Oracle官网下载并安装JDK 11或更高版本。Scala开发环境&#xff1a;可以…

Qt C++读写NFC标签NDEF网址URI

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.1d292c1biFgjSs&ftt&id615391857885 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> #include "QLibrary" …