Files
ylhp-ai-project-manager/src/main/java/cn/yinlihupo/service/system/impl/FeishuAuthServiceImpl.java
JiaoTianBo e171e35cd6 fix(feishu): 修正获取飞书应用访问令牌方式和更新开发环境密钥
- 修正了获取应用访问令牌时解析 JSON 的字段路径
- 将 app_access_token 从嵌套 JSON 直接改为顶层字段获取
- 更新了开发环境配置中的飞书应用密钥(app-secret)
2026-03-27 17:49:16 +08:00

207 lines
7.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cn.yinlihupo.service.system.impl;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.yinlihupo.common.config.FeishuConfig;
import cn.yinlihupo.domain.entity.SysUser;
import cn.yinlihupo.mapper.SysUserMapper;
import cn.yinlihupo.service.system.FeishuAuthService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
/**
* 飞书认证服务实现类
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FeishuAuthServiceImpl implements FeishuAuthService {
private final FeishuConfig feishuConfig;
private final SysUserMapper sysUserMapper;
/**
* 飞书OAuth授权端点
*/
private static final String FEISHU_OAUTH_URL = "https://open.feishu.cn/open-apis/authen/v1/authorize";
/**
* 获取访问令牌端点
*/
private static final String FEISHU_TOKEN_URL = "https://open.feishu.cn/open-apis/authen/v1/oidc/access_token";
/**
* 获取用户信息端点
*/
private static final String FEISHU_USER_INFO_URL = "https://open.feishu.cn/open-apis/authen/v1/user_info";
/**
* 应用访问令牌端点用于获取应用级token
*/
private static final String FEISHU_APP_TOKEN_URL = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";
@Override
public String buildAuthUrl(String state) {
String encodedRedirectUri = URLEncoder.encode(feishuConfig.getRedirectUri(), StandardCharsets.UTF_8);
return FEISHU_OAUTH_URL +
"?app_id=" + feishuConfig.getAppId() +
"&redirect_uri=" + encodedRedirectUri +
"&state=" + (state != null ? state : "") +
"&scope=contact:user.base contact:user.phone:readonly";
}
@Override
@Transactional(rollbackFor = Exception.class)
public SysUser loginByCode(String code) {
log.info("处理飞书登录, code: {}", code);
// 1. 获取应用访问令牌
String appAccessToken = getAppAccessToken();
// 2. 使用授权码获取用户访问令牌
String userAccessToken = getUserAccessToken(code, appAccessToken);
// 3. 获取用户信息
JSONObject userInfo = getUserInfo(userAccessToken);
// 4. 提取用户信息
String phone = userInfo.getStr("mobile");
String realName = userInfo.getStr("name");
String avatar = userInfo.getStr("avatar_url");
String email = userInfo.getStr("email");
String openId = userInfo.getStr("open_id");
log.info("飞书用户信息: phone={}, name={}, openId={}", phone, realName, openId);
// 5. 根据手机号获取或创建用户
return getOrCreateUserByPhone(phone, realName, avatar, email, openId);
}
/**
* 获取应用访问令牌
*/
private String getAppAccessToken() {
JSONObject requestBody = new JSONObject();
requestBody.set("app_id", feishuConfig.getAppId());
requestBody.set("app_secret", feishuConfig.getAppSecret());
try (HttpResponse response = HttpRequest.post(FEISHU_APP_TOKEN_URL)
.header("Content-Type", "application/json")
.body(requestBody.toString())
.execute()) {
String body = response.body();
log.debug("获取应用访问令牌响应: {}", body);
JSONObject jsonObject = JSONUtil.parseObj(body);
if (jsonObject.getInt("code") != 0) {
throw new RuntimeException("获取应用访问令牌失败: " + jsonObject.getStr("msg"));
}
return jsonObject.getStr("app_access_token");
}
}
/**
* 使用授权码获取用户访问令牌
*/
private String getUserAccessToken(String code, String appAccessToken) {
JSONObject requestBody = new JSONObject();
requestBody.set("grant_type", "authorization_code");
requestBody.set("code", code);
try (HttpResponse response = HttpRequest.post(FEISHU_TOKEN_URL)
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + appAccessToken)
.body(requestBody.toString())
.execute()) {
String body = response.body();
log.debug("获取用户访问令牌响应: {}", body);
JSONObject jsonObject = JSONUtil.parseObj(body);
if (jsonObject.getInt("code") != 0) {
throw new RuntimeException("获取用户访问令牌失败: " + jsonObject.getStr("msg"));
}
return jsonObject.getJSONObject("data").getStr("access_token");
}
}
/**
* 获取用户信息
*/
private JSONObject getUserInfo(String userAccessToken) {
try (HttpResponse response = HttpRequest.get(FEISHU_USER_INFO_URL)
.header("Authorization", "Bearer " + userAccessToken)
.execute()) {
String body = response.body();
log.debug("获取用户信息响应: {}", body);
JSONObject jsonObject = JSONUtil.parseObj(body);
if (jsonObject.getInt("code") != 0) {
throw new RuntimeException("获取用户信息失败: " + jsonObject.getStr("msg"));
}
return jsonObject.getJSONObject("data");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public SysUser getOrCreateUserByPhone(String phone, String realName, String avatar, String email, String openId) {
// 根据手机号查询用户
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getPhone, phone);
SysUser user = sysUserMapper.selectOne(queryWrapper);
LocalDateTime now = LocalDateTime.now();
if (user != null) {
// 用户已存在,更新信息
log.info("用户已存在,更新用户信息, phone: {}", phone);
user.setRealName(realName);
user.setAvatar(avatar);
user.setEmail(email);
user.setNickname(realName);
user.setLastLoginTime(now);
user.setUpdateTime(now);
// 使用openId作为用户名如果没有设置过
if (user.getUsername() == null || user.getUsername().isEmpty()) {
user.setUsername(openId);
}
sysUserMapper.updateById(user);
} else {
// 用户不存在,创建新用户
log.info("用户不存在,创建新用户, phone: {}", phone);
user = new SysUser();
user.setUsername(openId);
user.setRealName(realName);
user.setNickname(realName);
user.setAvatar(avatar);
user.setPhone(phone);
user.setEmail(email);
user.setStatus(1); // 正常状态
user.setLastLoginTime(now);
user.setCreateTime(now);
user.setUpdateTime(now);
user.setDeleted(0);
// 飞书登录用户不需要密码,设置一个随机密码
user.setPassword("FEISHU_OAUTH_USER");
sysUserMapper.insert(user);
}
return user;
}
}