- 修正了获取应用访问令牌时解析 JSON 的字段路径 - 将 app_access_token 从嵌套 JSON 直接改为顶层字段获取 - 更新了开发环境配置中的飞书应用密钥(app-secret)
207 lines
7.4 KiB
Java
207 lines
7.4 KiB
Java
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;
|
||
}
|
||
}
|