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"

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

相关推荐
考虑考虑21 小时前
Jpa使用union all
java·spring boot·后端
用户37215742613521 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊1 天前
Java学习第22天 - 云原生与容器化
java
渣哥1 天前
原来 Java 里线程安全集合有这么多种
java
间彧1 天前
Spring Boot集成Spring Security完整指南
java
间彧1 天前
Spring Secutiy基本原理及工作流程
java
Java水解1 天前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆1 天前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学1 天前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole1 天前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端