Commit 30c7bd23 by zhangxingmin

push

parent 22b90e6a
...@@ -8,6 +8,7 @@ import com.yd.communication.feign.response.*; ...@@ -8,6 +8,7 @@ import com.yd.communication.feign.response.*;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/** /**
...@@ -44,6 +45,30 @@ public class ApiCoSessionController implements ApiCoSessionFeignClient { ...@@ -44,6 +45,30 @@ public class ApiCoSessionController implements ApiCoSessionFeignClient {
} }
/** /**
* 根据房间号和密码获取会话详情
*
* @param roomId 房间号
* @param roomPwd 房间密码
* @return 完整会话信息
*/
@Override
public Result<SessionDetailResponse> get(String roomId, String roomPwd) {
return apiCoSessionService.get(roomId,roomPwd);
}
/**
* 判断资源创建者和参与者是否是同一个人(供参与者调用)
*
* @param roomId 房间号
* @param roomPwd 房间密码
* @return true-是同一人 false-不是同一人
*/
@Override
public Result<Boolean> isMy(String roomId, String roomPwd, HttpServletRequest httpRequest) {
return apiCoSessionService.isMy(roomId,roomPwd,httpRequest);
}
/**
* 获取会话状态 * 获取会话状态
* @param request * @param request
* @return * @return
......
...@@ -6,6 +6,8 @@ import com.yd.communication.feign.request.*; ...@@ -6,6 +6,8 @@ import com.yd.communication.feign.request.*;
import com.yd.communication.feign.response.*; import com.yd.communication.feign.response.*;
import com.yd.communication.service.model.CoSession; import com.yd.communication.service.model.CoSession;
import javax.servlet.http.HttpServletRequest;
public interface ApiCoSessionService { public interface ApiCoSessionService {
...@@ -26,4 +28,8 @@ public interface ApiCoSessionService { ...@@ -26,4 +28,8 @@ public interface ApiCoSessionService {
void updateCurrentPage(String roomId, String newPageJson, String operatorId); void updateCurrentPage(String roomId, String newPageJson, String operatorId);
CoSession getByRoomId(String roomId); CoSession getByRoomId(String roomId);
Result<SessionDetailResponse> get(String roomId, String roomPwd);
Result<Boolean> isMy(String roomId, String roomPwd, HttpServletRequest httpRequest);
} }
...@@ -25,7 +25,9 @@ import com.yd.communication.service.model.CoSession; ...@@ -25,7 +25,9 @@ import com.yd.communication.service.model.CoSession;
import com.yd.communication.service.service.ICoOperationLogService; import com.yd.communication.service.service.ICoOperationLogService;
import com.yd.communication.service.service.ICoSessionService; import com.yd.communication.service.service.ICoSessionService;
import com.yd.communication.service.service.IRecordingTaskService; import com.yd.communication.service.service.IRecordingTaskService;
import com.yd.communication.service.utils.CffpTokenUtil;
import com.yd.communication.service.utils.RandomUtil; import com.yd.communication.service.utils.RandomUtil;
import com.yd.communication.service.utils.SfpTokenUtil;
import com.yd.oss.feign.client.ApiOssFeignClient; import com.yd.oss.feign.client.ApiOssFeignClient;
import com.yd.oss.feign.request.ApiUploadFileRequest; import com.yd.oss.feign.request.ApiUploadFileRequest;
import com.yd.oss.feign.response.ApiUploadResponse; import com.yd.oss.feign.response.ApiUploadResponse;
...@@ -43,6 +45,7 @@ import org.springframework.web.client.RestTemplate; ...@@ -43,6 +45,7 @@ import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
...@@ -731,6 +734,91 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService { ...@@ -731,6 +734,91 @@ public class ApiCoSessionServiceImpl implements ApiCoSessionService {
} }
} }
/**
* 根据房间号和密码获取会话详情
*
* @param roomId 房间号
* @param roomPwd 房间密码
* @return 完整会话信息
*/
@Override
public Result<SessionDetailResponse> get(String roomId, String roomPwd) {
log.info("【根据房间号和密码获取会话详情】roomId={}, roomPwd={}", roomId,roomPwd);
if (StringUtils.isBlank(roomId)) {
throw new BusinessException("房间号不能为空");
}
if (StringUtils.isBlank(roomPwd)) {
throw new BusinessException("房间密码不能为空");
}
//查询会话详情
LambdaQueryWrapper<CoSession> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CoSession::getRoomId, roomId)
.eq(CoSession::getRoomPwd,roomPwd)
.last(" limit 1 ");
CoSession session = iCoSessionService.getOne(wrapper);
if (session == null) {
throw new BusinessException("会话信息不存在");
}
SessionDetailResponse response = new SessionDetailResponse();
BeanUtils.copyProperties(session,response);
return Result.success(response);
}
/**
* 判断资源创建者和参与者是否是同一个人
*
* @param roomId 房间号
* @param roomPwd 房间密码
* @return true-是同一人 false-不是同一人
*/
@Override
public Result<Boolean> isMy(String roomId, String roomPwd, HttpServletRequest httpRequest) {
log.info("【判断资源创建者和参与者是否是同一个人】roomId={}, roomPwd={}", roomId,roomPwd);
if (StringUtils.isBlank(roomId)) {
throw new BusinessException("房间号不能为空");
}
if (StringUtils.isBlank(roomPwd)) {
throw new BusinessException("房间密码不能为空");
}
//解析SFP参与者的token信息,提取用户ID
//解析 Token
String authorization = httpRequest.getHeader("X-Authorization");
if (StringUtils.isBlank(authorization)) {
throw new BusinessException("访问者(参与者)token信息不能为空");
}
//参与者用户ID
Long userId = null;
String userIdStr = SfpTokenUtil.getUserIdFromToken(authorization);
if (StringUtils.isNotBlank(userIdStr)) {
try {
userId = Long.valueOf(userIdStr);
} catch (NumberFormatException ignored) {
log.info("【判断资源创建者和参与者是否是同一个人】errorMsg={}", ignored.getMessage());
}
}
if (userId == null) {
throw new BusinessException("访问者(参与者)token的用户ID解析为空");
}
//查询会话信息
LambdaQueryWrapper<CoSession> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CoSession::getRoomId, roomId)
.eq(CoSession::getRoomPwd,roomPwd)
.last(" limit 1 ");
CoSession session = iCoSessionService.getOne(wrapper);
if (session == null) {
throw new BusinessException("会话信息不存在");
}
//比较参与者的用户ID和创建者的用户ID是否相同,相同代表同一个人
String creatorId = session.getOwnerId();
if (userId.equals(creatorId)) {
//是同一个人
return Result.success(true);
}
return Result.success(false);
}
private boolean autoStartRecording() { private boolean autoStartRecording() {
return true; // 从配置读取 return true; // 从配置读取
} }
......
...@@ -8,6 +8,8 @@ import org.springframework.cloud.openfeign.FeignClient; ...@@ -8,6 +8,8 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
/** /**
* 通信服务-协同会话信息 Feign 客户端 * 通信服务-协同会话信息 Feign 客户端
...@@ -37,6 +39,29 @@ public interface ApiCoSessionFeignClient { ...@@ -37,6 +39,29 @@ public interface ApiCoSessionFeignClient {
Result<JoinResponse> join(@Validated @RequestBody JoinRequest request); Result<JoinResponse> join(@Validated @RequestBody JoinRequest request);
/** /**
* 根据房间号和密码获取会话详情
*
* @param roomId 房间号
* @param roomPwd 房间密码
* @return 完整会话信息
*/
@GetMapping("/detail/{roomId}/{roomPwd}")
Result<SessionDetailResponse> get(@PathVariable("roomId") String roomId,
@PathVariable("roomPwd") String roomPwd);
/**
* 判断资源创建者和参与者是否是同一个人(供参与者调用)
*
* @param roomId 房间号
* @param roomPwd 房间密码
* @return true-是同一人 false-不是同一人
*/
@GetMapping("/isMy/{roomId}/{roomPwd}")
Result<Boolean> isMy(@PathVariable("roomId") String roomId,
@PathVariable("roomPwd") String roomPwd,
HttpServletRequest httpRequest);
/**
* 获取会话状态 * 获取会话状态
* @param request * @param request
* @return * @return
......
...@@ -8,6 +8,8 @@ import lombok.extern.slf4j.Slf4j; ...@@ -8,6 +8,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/** /**
* 通信服务-协同会话信息Feign降级处理 * 通信服务-协同会话信息Feign降级处理
*/ */
...@@ -24,6 +26,16 @@ public class ApiCoSessionFeignFallbackFactory implements FallbackFactory<ApiCoSe ...@@ -24,6 +26,16 @@ public class ApiCoSessionFeignFallbackFactory implements FallbackFactory<ApiCoSe
} }
@Override @Override
public Result<SessionDetailResponse> get(String roomId, String roomPwd) {
return null;
}
@Override
public Result<Boolean> isMy(String roomId, String roomPwd, HttpServletRequest httpRequest) {
return null;
}
@Override
public Result<JoinResponse> join(JoinRequest request) { public Result<JoinResponse> join(JoinRequest request) {
return null; return null;
} }
......
package com.yd.communication.service.utils;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* CFFP 项目专用 JWT Token 工具类
* 完全在代码内部获取环境标识(System.getProperty),硬编码不同环境的配置
* 支持生成 Token 和解析 Token(获取 userId、cffpUserId 等)
*
* @author yourname
*/
@Slf4j
@Service
public class CffpTokenUtil {
// JWT 标准声明 Key
private static final String CLAIM_KEY_SUBJECT = "sub";
private static final String CLAIM_KEY_CREATED = "created";
// 自定义声明 Key(与 CffpTokenFilter 保持一致)
private static final String CLAIM_KEY_USERID = "UserId";
private static final String CLAIM_KEY_CFFP_USERID = "cffpUserId";
// 静态配置字段(一套配置,根据环境加载不同的值)
private static String seal;
private static String issuer;
private static Long expiration;
/**
* 初始化:通过 System.getProperty 获取环境,硬编码对应的配置
* 该方法在 Spring 容器启动完成后自动执行
*/
@PostConstruct
public void init() {
// 从系统属性中获取 spring.profiles.active,如果获取不到则默认为 "default"
String activeProfile = System.getProperty("spring.profiles.active", "default");
// 判断是否为生产环境
boolean isProd = "prod".equalsIgnoreCase(activeProfile)
|| "production".equalsIgnoreCase(activeProfile)
|| activeProfile.toLowerCase().contains("prod");
if (isProd) {
// 生产环境配置
seal = "zhb123!@#";
issuer = "cffpwealthcenter";
expiration = 2592000L;
log.info("CffpTokenUtil 加载生产环境配置, issuer: {}, expiration: {} 秒", issuer, expiration);
} else {
// 测试/开发环境配置
seal = "zhb123!@#";
issuer = "zuihuibi";
expiration = 86400000L;
log.info("CffpTokenUtil 加载测试环境配置, issuer: {}, expiration: {} 秒", issuer, expiration);
}
}
// ======================== 生成 Token ========================
/**
* 生成 JWT Token(自动添加 issuer 前缀,如 "zuihuibi {token}")
*
* @param ticket 用户标识(通常为登录名或唯一标识)
* @param userId 用户 ID(字符串)
* @param cffpUserId CFFP 用户 ID(可选)
* @return 完整 Token 字符串(含前缀)
*/
public static String generateToken(String ticket, String userId, String cffpUserId) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_SUBJECT, ticket);
claims.put(CLAIM_KEY_USERID, userId);
if (cffpUserId != null) {
claims.put(CLAIM_KEY_CFFP_USERID, cffpUserId);
}
claims.put(CLAIM_KEY_CREATED, new Date());
String token = Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, seal)
.compact();
return issuer + " " + token;
}
// ======================== 解析 Token ========================
/**
* 从 Token 中解析 Claims
*
* @param token 原始 Token(可能包含 issuer 前缀)
* @return Map, key 包含 "resultCode"(SUCCESS/EXPIRED/INVALID)和 "claims"(解析成功时为 Claims 对象)
*/
public static Map<String, Object> getClaimsFromToken(String token) {
Map<String, Object> map = new HashMap<>();
String resultCode = "SUCCESS";
Claims claims = null;
if (token == null || token.isEmpty()) {
map.put("resultCode", "INVALID");
map.put("claims", null);
return map;
}
try {
// 去除 issuer 前缀(如果存在)
String realToken = token;
if (token.startsWith(issuer + " ")) {
realToken = token.substring(issuer.length() + 1);
}
Jws<Claims> jwsClaims = Jwts.parser()
.setSigningKey(seal)
.parseClaimsJws(realToken);
claims = jwsClaims.getBody();
} catch (ExpiredJwtException e) {
resultCode = "EXPIRED";
} catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
resultCode = "INVALID";
} catch (Exception e) {
resultCode = "INVALID";
}
map.put("resultCode", resultCode);
map.put("claims", claims);
return map;
}
/**
* 从 Token 中获取用户 ID(String 形式)
*
* @param token 原始 Token
* @return 用户 ID 字符串,解析失败返回 null
*/
public static String getUserIdFromToken(String token) {
Map<String, Object> map = getClaimsFromToken(token);
if ("SUCCESS".equals(map.get("resultCode"))) {
Claims claims = (Claims) map.get("claims");
if (claims != null) {
Object userId = claims.get(CLAIM_KEY_USERID);
return userId != null ? userId.toString() : null;
}
}
return null;
}
/**
* 从 Token 中获取 CFFP 用户 ID
*
* @param token 原始 Token
* @return CFFP 用户 ID 字符串,解析失败返回 null
*/
public static String getCffpUserIdFromToken(String token) {
Map<String, Object> map = getClaimsFromToken(token);
if ("SUCCESS".equals(map.get("resultCode"))) {
Claims claims = (Claims) map.get("claims");
if (claims != null) {
Object cffpUserId = claims.get(CLAIM_KEY_CFFP_USERID);
return cffpUserId != null ? cffpUserId.toString() : null;
}
}
return null;
}
/**
* 验证 Token 是否有效(未过期、格式正确)
*
* @param token 原始 Token
* @return true 表示有效,false 表示无效或过期
*/
public static boolean validateToken(String token) {
Map<String, Object> map = getClaimsFromToken(token);
return "SUCCESS".equals(map.get("resultCode"));
}
/**
* 检查 Token 是否过期
*
* @param token 原始 Token
* @return true 表示已过期,false 表示未过期或解析失败
*/
public static boolean isTokenExpired(String token) {
Map<String, Object> map = getClaimsFromToken(token);
String resultCode = (String) map.get("resultCode");
if ("EXPIRED".equals(resultCode)) {
return true;
}
if ("SUCCESS".equals(resultCode)) {
Claims claims = (Claims) map.get("claims");
if (claims != null) {
Date expirationDate = claims.getExpiration();
return expirationDate != null && expirationDate.before(new Date());
}
}
return false;
}
}
\ No newline at end of file
package com.yd.communication.service.utils;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* SFP 项目专用 JWT Token 工具类
* 完全在代码内部获取环境标识(System.getProperty),硬编码不同环境的配置
* 支持生成 Token 和解析 Token(获取 userId、customerId 等)
*
* @author yourname
*/
@Slf4j
@Service
public class SfpTokenUtil {
// JWT 标准声明 Key
private static final String CLAIM_KEY_SUBJECT = "sub";
private static final String CLAIM_KEY_CREATED = "created";
// 自定义声明 Key(与 JwtTokenUtil 保持一致)
private static final String CLAIM_KEY_USERID = "UserId";
private static final String CLAIM_KEY_CUSTOMERID = "CustomerId";
// 静态配置字段
private static String seal;
private static String issuer;
private static Long expiration;
/**
* 初始化:通过 System.getProperty 获取环境,硬编码对应的配置
* 该方法在 Spring 容器启动完成后自动执行
*/
@PostConstruct
public void init() {
// 从系统属性中获取 spring.profiles.active,如果获取不到则默认为 "default"
String activeProfile = System.getProperty("spring.profiles.active", "default");
// 判断是否为生产环境(可自定义判断规则,如包含 "prod" 或 "production")
boolean isProd = "prod".equalsIgnoreCase(activeProfile)
|| "production".equalsIgnoreCase(activeProfile)
|| activeProfile.toLowerCase().contains("prod");
if (isProd) {
// 生产环境配置(请替换为实际的生产密钥)
seal = "SFP2023!@#";
issuer = "sfpfamilyfinancialplanning";
expiration = 2592000L; // 24小时
log.info("SfpTokenUtil 加载生产环境配置, issuer: {}, expiration: {} 秒", issuer, expiration);
} else {
// 测试/开发环境配置(请替换为实际的测试密钥)
seal = "zhb123!@#";
issuer = "zuihuibi";
expiration = 2592000L;
log.info("SfpTokenUtil 加载测试环境配置, issuer: {}, expiration: {} 秒", issuer, expiration);
}
}
// ======================== 生成 Token ========================
/**
* 生成 JWT Token(自动添加 issuer 前缀,如 "zuihuibi {token}")
*
* @param ticket 用户标识(通常为登录名或唯一标识)
* @param userId 用户 ID(字符串)
* @param customerId 客户 ID(可选)
* @return 完整 Token 字符串(含前缀)
*/
public static String generateToken(String ticket, String userId, String customerId) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_SUBJECT, ticket);
claims.put(CLAIM_KEY_USERID, userId);
if (customerId != null) {
claims.put(CLAIM_KEY_CUSTOMERID, customerId);
}
claims.put(CLAIM_KEY_CREATED, new Date());
String token = Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, seal)
.compact();
return issuer + " " + token;
}
// ======================== 解析 Token ========================
/**
* 从 Token 中解析 Claims(包含所有声明信息)
*
* @param token 原始 Token(可能包含 issuer 前缀)
* @return Map, key 包含 "resultCode"(SUCCESS/EXPIRED/INVALID)和 "claims"(解析成功时为 Claims 对象)
*/
public static Map<String, Object> getClaimsFromToken(String token) {
Map<String, Object> map = new HashMap<>();
String resultCode = "SUCCESS";
Claims claims = null;
try {
// 去除 issuer 前缀(如果存在)
String realToken = token;
if (token.startsWith(issuer + " ")) {
realToken = token.substring(issuer.length() + 1);
}
Jws<Claims> jwsClaims = Jwts.parser()
.setSigningKey(seal)
.parseClaimsJws(realToken);
claims = jwsClaims.getBody();
} catch (ExpiredJwtException e) {
resultCode = "EXPIRED";
} catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
resultCode = "INVALID";
} catch (Exception e) {
resultCode = "INVALID";
}
map.put("resultCode", resultCode);
map.put("claims", claims);
return map;
}
/**
* 从 Token 中获取用户 ID(String 形式)
*
* @param token 原始 Token
* @return 用户 ID 字符串,解析失败返回 null
*/
public static String getUserIdFromToken(String token) {
Map<String, Object> map = getClaimsFromToken(token);
if ("SUCCESS".equals(map.get("resultCode"))) {
Claims claims = (Claims) map.get("claims");
if (claims != null) {
Object userId = claims.get(CLAIM_KEY_USERID);
return userId != null ? userId.toString() : null;
}
}
return null;
}
/**
* 从 Token 中获取客户 ID
*
* @param token 原始 Token
* @return 客户 ID 字符串,解析失败返回 null
*/
public static String getCustomerIdFromToken(String token) {
Map<String, Object> map = getClaimsFromToken(token);
if ("SUCCESS".equals(map.get("resultCode"))) {
Claims claims = (Claims) map.get("claims");
if (claims != null) {
Object customerId = claims.get(CLAIM_KEY_CUSTOMERID);
return customerId != null ? customerId.toString() : null;
}
}
return null;
}
/**
* 验证 Token 是否有效(未过期、格式正确)
*
* @param token 原始 Token
* @return true 表示有效,false 表示无效或过期
*/
public static boolean validateToken(String token) {
Map<String, Object> map = getClaimsFromToken(token);
return "SUCCESS".equals(map.get("resultCode"));
}
/**
* 检查 Token 是否过期
*
* @param token 原始 Token
* @return true 表示已过期,false 表示未过期或解析失败
*/
public static boolean isTokenExpired(String token) {
Map<String, Object> map = getClaimsFromToken(token);
String resultCode = (String) map.get("resultCode");
if ("EXPIRED".equals(resultCode)) {
return true;
}
if ("SUCCESS".equals(resultCode)) {
Claims claims = (Claims) map.get("claims");
if (claims != null) {
Date expirationDate = claims.getExpiration();
return expirationDate != null && expirationDate.before(new Date());
}
}
return false;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment