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"
学习笔记仅供学习使用,更详细步骤请移步官方文档。