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"

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

相关推荐
一决威严-雪雪4 分钟前
springboot整合admin
java·spring boot·后端
吴秋霖4 分钟前
某漫画网站JS逆向反混淆流程分析
开发语言·javascript·ecmascript
栗筝i5 分钟前
Spring 中的常用注解
java·后端·spring
Growthofnotes9 分钟前
C++—14、C++ 中的指针最基础的原理
开发语言·c++
小登ai学习23 分钟前
框架部分面试题学习
java·学习·面试
MilesMatheson36 分钟前
ubuntu 编译android源码报错:loadlocale.c:129: _nl_intern_locale_data:
c语言·开发语言·算法
sysu631 小时前
73.矩阵置零 python
开发语言·数据结构·python·线性代数·leetcode·面试·矩阵
new6669991 小时前
Java实现命令模式
java·命令模式
写代码的熊萌新1 小时前
JAVA2-类与对象编程(1)
java·开发语言
知初~1 小时前
SpringBoot3
java·spring boot·spring·log4j·maven