feat: 解耦认证逻辑
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
package com.webgame.webgamebackend.common.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前请求用户上下文
|
||||||
|
* <p>
|
||||||
|
* 基于 ThreadLocal 存储当前请求的用户 ID,由 AuthInterceptor 在 preHandle 中设置,
|
||||||
|
* 在 afterCompletion 中清理。业务代码可通过 {@link #get()} 获取当前用户 ID。
|
||||||
|
* <p>
|
||||||
|
* 注意:仅在同一请求线程内有效,异步场景需手动传递。
|
||||||
|
*/
|
||||||
|
public final class AuthContext {
|
||||||
|
|
||||||
|
private static final ThreadLocal<String> USER_ID = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private AuthContext() {
|
||||||
|
// 工具类,禁止实例化
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前请求的用户 ID
|
||||||
|
*/
|
||||||
|
public static void set(String userId) {
|
||||||
|
USER_ID.set(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前请求的用户 ID
|
||||||
|
*
|
||||||
|
* @return 用户 ID,未设置时返回 null
|
||||||
|
*/
|
||||||
|
public static String get() {
|
||||||
|
return USER_ID.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理当前请求的用户上下文
|
||||||
|
* <p>
|
||||||
|
* 必须在请求结束后调用,防止 ThreadLocal 内存泄漏。
|
||||||
|
*/
|
||||||
|
public static void clear() {
|
||||||
|
USER_ID.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package com.webgame.webgamebackend.common.auth;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
import cn.dev33.satoken.exception.NotRoleException;
|
||||||
|
import com.webgame.webgamebackend.common.exception.BusinessException;
|
||||||
|
import com.webgame.webgamebackend.common.exception.ErrorCode;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证拦截器,替代 Sa-Token 的 SaInterceptor
|
||||||
|
* <p>
|
||||||
|
* 职责:
|
||||||
|
* <ol>
|
||||||
|
* <li>放行公开路径和 OPTIONS 预检请求</li>
|
||||||
|
* <li>调用 {@link AuthenticationProvider#getCurrentUserId()} 触发登录校验</li>
|
||||||
|
* <li>将 Sa-Token 框架异常转换为 {@link BusinessException},实现认证框架与业务解耦</li>
|
||||||
|
* <li>校验通过后将用户 ID 存入 {@link AuthContext}</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
|
private final AuthenticationProvider authProvider;
|
||||||
|
private final AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公开路由条目
|
||||||
|
*
|
||||||
|
* @param path Ant 风格路径模式
|
||||||
|
* @param methods 允许的 HTTP 方法,空数组表示所有方法
|
||||||
|
*/
|
||||||
|
private record PublicRoute(String path, String... methods) {
|
||||||
|
boolean matches(String requestPath, String requestMethod) {
|
||||||
|
if (!new AntPathMatcher().match(path, requestPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (methods.length == 0) {
|
||||||
|
return true; // 所有方法放行
|
||||||
|
}
|
||||||
|
for (String m : methods) {
|
||||||
|
if (m.equalsIgnoreCase(requestMethod)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 无需登录的公开路由 */
|
||||||
|
private static final List<PublicRoute> PUBLIC_ROUTES = List.of(
|
||||||
|
// 框架级路径(所有方法)
|
||||||
|
new PublicRoute("/actuator/**"),
|
||||||
|
new PublicRoute("/swagger-ui/**"),
|
||||||
|
new PublicRoute("/swagger-ui.html"),
|
||||||
|
new PublicRoute("/v3/api-docs/**"),
|
||||||
|
new PublicRoute("/webjars/**"),
|
||||||
|
new PublicRoute("/error"),
|
||||||
|
new PublicRoute("/sse/**"),
|
||||||
|
|
||||||
|
// 账号模块 — 登录/注册/刷新无需认证
|
||||||
|
new PublicRoute("/account/login", "POST"),
|
||||||
|
new PublicRoute("/account/register", "POST"),
|
||||||
|
new PublicRoute("/account/refresh", "POST"),
|
||||||
|
|
||||||
|
// 游戏模块 — 查看列表/详情无需认证
|
||||||
|
new PublicRoute("/game/list", "GET"),
|
||||||
|
new PublicRoute("/game/room/list", "GET"),
|
||||||
|
// GET /game/room/{roomId} 详情页,排除 POST/PUT/DELETE 等写操作
|
||||||
|
new PublicRoute("/game/room/**", "GET")
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
Object handler) {
|
||||||
|
String method = request.getMethod();
|
||||||
|
String path = request.getRequestURI();
|
||||||
|
|
||||||
|
// OPTIONS 预检请求放行
|
||||||
|
if ("OPTIONS".equalsIgnoreCase(method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公开路由放行
|
||||||
|
if (isPublicRoute(path, method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 认证校验:调用 authProvider 触发 Sa-Token 的登录检查
|
||||||
|
try {
|
||||||
|
String userId = authProvider.getCurrentUserId();
|
||||||
|
AuthContext.set(userId);
|
||||||
|
return true;
|
||||||
|
} catch (NotLoginException e) {
|
||||||
|
log.debug("未登录访问受保护路径: path={}", path);
|
||||||
|
throw new BusinessException(ErrorCode.AUTH_NOT_LOGIN);
|
||||||
|
} catch (NotPermissionException | NotRoleException e) {
|
||||||
|
log.warn("权限不足: path={}, message={}", path, e.getMessage());
|
||||||
|
throw new BusinessException(ErrorCode.AUTH_FORBIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
Object handler, Exception ex) {
|
||||||
|
// 请求结束后清理 ThreadLocal,防止内存泄漏
|
||||||
|
AuthContext.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 判断路由是否为公开路由(无需登录) */
|
||||||
|
private boolean isPublicRoute(String path, String method) {
|
||||||
|
for (PublicRoute route : PUBLIC_ROUTES) {
|
||||||
|
if (route.matches(path, method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.webgame.webgamebackend.common.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证提供者抽象接口
|
||||||
|
* <p>
|
||||||
|
* 隔离具体的认证框架实现(当前为 Sa-Token),业务代码只依赖此接口。
|
||||||
|
* 如需切换认证框架,只需提供新的实现类即可,业务代码无需修改。
|
||||||
|
*/
|
||||||
|
public interface AuthenticationProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录,使 userId 在当前会话中保持登录态
|
||||||
|
*
|
||||||
|
* @param userId 用户唯一标识
|
||||||
|
* @return 生成的 Access Token
|
||||||
|
*/
|
||||||
|
String login(Object userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前会话登出,使 Access Token 失效
|
||||||
|
*/
|
||||||
|
void logout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前已登录的用户 ID
|
||||||
|
*
|
||||||
|
* @return 当前用户 ID
|
||||||
|
* @throws cn.dev33.satoken.exception.NotLoginException 未登录时由实现层抛出
|
||||||
|
*/
|
||||||
|
String getCurrentUserId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 Token 获取用户 ID(不检查当前会话登录态)
|
||||||
|
*
|
||||||
|
* @param token Access Token
|
||||||
|
* @return 用户 ID,Token 无效时返回 null
|
||||||
|
*/
|
||||||
|
String getUserIdByToken(String token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前会话的 Access Token
|
||||||
|
*
|
||||||
|
* @return Access Token 字符串
|
||||||
|
*/
|
||||||
|
String getCurrentToken();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在当前请求上下文中设置 Token,使后续 getCurrentUserId() 可用
|
||||||
|
*
|
||||||
|
* @param token Access Token
|
||||||
|
* @param timeout Token 有效期(秒),-1 表示保持原有有效期
|
||||||
|
*/
|
||||||
|
void setContextToken(String token, int timeout);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.webgame.webgamebackend.common.auth;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token 认证提供者实现
|
||||||
|
* <p>
|
||||||
|
* 将 Sa-Token 的 StpUtil 静态方法封装为 AuthenticationProvider 接口,
|
||||||
|
* 是项目中唯一直接依赖 Sa-Token 的业务代码之外的文件。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class SaTokenAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String login(Object userId) {
|
||||||
|
StpUtil.login(userId);
|
||||||
|
String token = StpUtil.getTokenValue();
|
||||||
|
log.debug("用户登录成功: userId={}", userId);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout() {
|
||||||
|
StpUtil.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentUserId() {
|
||||||
|
return StpUtil.getLoginIdAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserIdByToken(String token) {
|
||||||
|
Object loginId = StpUtil.getLoginIdByToken(token);
|
||||||
|
return loginId != null ? loginId.toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentToken() {
|
||||||
|
return StpUtil.getTokenValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContextToken(String token, int timeout) {
|
||||||
|
StpUtil.setTokenValue(token, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.webgame.webgamebackend.common.config;
|
package com.webgame.webgamebackend.common.config;
|
||||||
|
|
||||||
import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction;
|
import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction;
|
||||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
|
||||||
import cn.dev33.satoken.router.SaHttpMethod;
|
import cn.dev33.satoken.router.SaHttpMethod;
|
||||||
import cn.dev33.satoken.router.SaRouter;
|
import cn.dev33.satoken.router.SaRouter;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import com.webgame.webgamebackend.common.auth.AuthInterceptor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -12,28 +12,22 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sa-Token 鉴权配置
|
* Web 鉴权配置
|
||||||
|
* <p>
|
||||||
|
* 使用自定义 {@link AuthInterceptor} 替代 Sa-Token 的 {@code SaInterceptor},
|
||||||
|
* 在拦截器层将 Sa-Token 框架异常转为业务异常,实现认证框架与业务代码解耦。
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
private final AuthInterceptor authInterceptor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(new SaInterceptor(handler -> {
|
registry.addInterceptor(authInterceptor)
|
||||||
SaRouter.match(SaHttpMethod.OPTIONS).free(r -> {
|
.addPathPatterns("/**")
|
||||||
}).back();
|
|
||||||
|
|
||||||
SaRouter.match("/**")
|
|
||||||
.notMatch("/actuator/**")
|
|
||||||
.notMatch("/swagger-ui/**")
|
|
||||||
.notMatch("/swagger-ui.html")
|
|
||||||
.notMatch("/v3/api-docs/**")
|
|
||||||
.notMatch("/webjars/**")
|
|
||||||
.notMatch("/error")
|
|
||||||
.notMatch("/sse/**")
|
|
||||||
.check(r -> StpUtil.checkLogin());
|
|
||||||
})).addPathPatterns("/**")
|
|
||||||
.excludePathPatterns("/error");
|
.excludePathPatterns("/error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ public enum ErrorCode {
|
|||||||
AVATAR_INVALID_TYPE(2003, "头像格式不支持,仅支持 JPG、PNG、WebP、GIF", 400),
|
AVATAR_INVALID_TYPE(2003, "头像格式不支持,仅支持 JPG、PNG、WebP、GIF", 400),
|
||||||
AVATAR_TOO_LARGE(2004, "头像文件大小不能超过 2MB", 400),
|
AVATAR_TOO_LARGE(2004, "头像文件大小不能超过 2MB", 400),
|
||||||
|
|
||||||
|
// ========== 认证模块 ==========
|
||||||
|
AUTH_NOT_LOGIN(4001, "请先登录", 401),
|
||||||
|
AUTH_FORBIDDEN(4002, "权限不足", 403),
|
||||||
|
|
||||||
// ========== 游戏模块 ==========
|
// ========== 游戏模块 ==========
|
||||||
GAME_NOT_FOUND(3001, "游戏不存在或已下架", 404),
|
GAME_NOT_FOUND(3001, "游戏不存在或已下架", 404),
|
||||||
ROOM_NOT_FOUND(3002, "房间不存在", 404),
|
ROOM_NOT_FOUND(3002, "房间不存在", 404),
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package com.webgame.webgamebackend.common.handler;
|
package com.webgame.webgamebackend.common.handler;
|
||||||
|
|
||||||
import cn.dev33.satoken.exception.NotLoginException;
|
|
||||||
import cn.dev33.satoken.exception.NotPermissionException;
|
|
||||||
import cn.dev33.satoken.exception.NotRoleException;
|
|
||||||
import cn.dev33.satoken.exception.SaTokenContextException;
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
||||||
import com.webgame.webgamebackend.common.exception.BusinessException;
|
import com.webgame.webgamebackend.common.exception.BusinessException;
|
||||||
@@ -32,6 +28,9 @@ import java.io.IOException;
|
|||||||
* <p>
|
* <p>
|
||||||
* 业务异常(仅来自 REST JSON 请求)返回 ResponseEntity,经过 Spring 内容协商。
|
* 业务异常(仅来自 REST JSON 请求)返回 ResponseEntity,经过 Spring 内容协商。
|
||||||
* 框架级异常(可能来自 SSE/浏览器等任意请求类型)直接写 HttpServletResponse 绕过内容协商。
|
* 框架级异常(可能来自 SSE/浏览器等任意请求类型)直接写 HttpServletResponse 绕过内容协商。
|
||||||
|
* <p>
|
||||||
|
* 认证相关异常(NotLoginException 等)已由 {@code AuthInterceptor} 转为
|
||||||
|
* {@link BusinessException},此处不再直接依赖 Sa-Token。
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
@@ -44,12 +43,6 @@ public class GlobalExceptionHandler {
|
|||||||
// SSE 超时在长连接场景下属于正常情况,不写响应体
|
// SSE 超时在长连接场景下属于正常情况,不写响应体
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(SaTokenContextException.class)
|
|
||||||
public void handleSaTokenContext(SaTokenContextException ex) {
|
|
||||||
log.warn("Sa-Token 上下文不可用: {}", ex.getMessage());
|
|
||||||
// 长连接场景,不写响应体
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExceptionHandler(AsyncRequestNotUsableException.class)
|
@ExceptionHandler(AsyncRequestNotUsableException.class)
|
||||||
public void handleAsyncRequestNotUsable(AsyncRequestNotUsableException ex) {
|
public void handleAsyncRequestNotUsable(AsyncRequestNotUsableException ex) {
|
||||||
// SSE 响应已被客户端关闭,不写响应体
|
// SSE 响应已被客户端关闭,不写响应体
|
||||||
@@ -105,28 +98,7 @@ public class GlobalExceptionHandler {
|
|||||||
.body(ApiResponse.fail(ErrorCode.AVATAR_TOO_LARGE.getCode(), "文件大小不能超过 2MB"));
|
.body(ApiResponse.fail(ErrorCode.AVATAR_TOO_LARGE.getCode(), "文件大小不能超过 2MB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 认证/授权异常 → HTTP 401 / 403 ====================
|
|
||||||
|
|
||||||
@ExceptionHandler(NotLoginException.class)
|
|
||||||
public ResponseEntity<ApiResponse<Void>> handleNotLogin(NotLoginException ex) {
|
|
||||||
log.warn("未登录: {}", ex.getMessage());
|
|
||||||
return ResponseEntity
|
|
||||||
.status(HttpStatus.UNAUTHORIZED)
|
|
||||||
.body(ApiResponse.fail(401, "请先登录"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExceptionHandler({NotPermissionException.class, NotRoleException.class})
|
|
||||||
public ResponseEntity<ApiResponse<Void>> handleForbidden(Exception ex) {
|
|
||||||
log.warn("权限不足: {}", ex.getMessage());
|
|
||||||
return ResponseEntity
|
|
||||||
.status(HttpStatus.FORBIDDEN)
|
|
||||||
.body(ApiResponse.fail(403, "权限不足"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 框架级异常(直接写响应,绕过内容协商) ====================
|
// ==================== 框架级异常(直接写响应,绕过内容协商) ====================
|
||||||
// 这类异常可能来自任意请求类型(SSE Accept: text/event-stream、
|
|
||||||
// 浏览器 Accept: text/html 等),若走 ResponseEntity 会因 Accept 头
|
|
||||||
// 与 JSON 响应体不匹配而抛出 HttpMediaTypeNotAcceptableException。
|
|
||||||
|
|
||||||
@ExceptionHandler(NoResourceFoundException.class)
|
@ExceptionHandler(NoResourceFoundException.class)
|
||||||
public void handleNoResourceFound(NoResourceFoundException ex, HttpServletResponse response) throws IOException {
|
public void handleNoResourceFound(NoResourceFoundException ex, HttpServletResponse response) throws IOException {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.webgame.webgamebackend.controller;
|
package com.webgame.webgamebackend.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
|
||||||
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
||||||
import com.webgame.webgamebackend.common.dto.account.LoginRequest;
|
import com.webgame.webgamebackend.common.dto.account.LoginRequest;
|
||||||
import com.webgame.webgamebackend.common.dto.account.LoginResponse;
|
import com.webgame.webgamebackend.common.dto.account.LoginResponse;
|
||||||
@@ -30,7 +29,6 @@ public class AccountController {
|
|||||||
/**
|
/**
|
||||||
* 登录
|
* 登录
|
||||||
*/
|
*/
|
||||||
@SaIgnore
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public ApiResponse<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
public ApiResponse<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||||
LoginResponse result = accountService.login(request);
|
LoginResponse result = accountService.login(request);
|
||||||
@@ -40,7 +38,6 @@ public class AccountController {
|
|||||||
/**
|
/**
|
||||||
* 注册
|
* 注册
|
||||||
*/
|
*/
|
||||||
@SaIgnore
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public ApiResponse<LoginResponse> register(@Valid @RequestBody RegisterRequest request) {
|
public ApiResponse<LoginResponse> register(@Valid @RequestBody RegisterRequest request) {
|
||||||
LoginResponse result = accountService.register(request);
|
LoginResponse result = accountService.register(request);
|
||||||
@@ -50,7 +47,6 @@ public class AccountController {
|
|||||||
/**
|
/**
|
||||||
* 刷新令牌,用 refresh token 换取新的 access token 和 refresh token
|
* 刷新令牌,用 refresh token 换取新的 access token 和 refresh token
|
||||||
*/
|
*/
|
||||||
@SaIgnore
|
|
||||||
@PostMapping("/refresh")
|
@PostMapping("/refresh")
|
||||||
public ApiResponse<LoginResponse> refresh(@Valid @RequestBody RefreshTokenRequest request) {
|
public ApiResponse<LoginResponse> refresh(@Valid @RequestBody RefreshTokenRequest request) {
|
||||||
LoginResponse result = accountService.refresh(request.refreshToken());
|
LoginResponse result = accountService.refresh(request.refreshToken());
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.webgame.webgamebackend.controller;
|
package com.webgame.webgamebackend.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
||||||
import com.webgame.webgamebackend.common.dto.game.*;
|
import com.webgame.webgamebackend.common.dto.game.*;
|
||||||
import com.webgame.webgamebackend.service.GameService;
|
import com.webgame.webgamebackend.service.GameService;
|
||||||
@@ -22,13 +21,13 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
public class GameController {
|
public class GameController {
|
||||||
|
|
||||||
private final GameService gameService;
|
private final GameService gameService;
|
||||||
|
private final AuthenticationProvider authProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取游戏列表
|
* 获取游戏列表
|
||||||
*
|
*
|
||||||
* 无需登录即可调用。
|
* 无需登录即可调用。
|
||||||
*/
|
*/
|
||||||
@SaIgnore
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public ApiResponse<GameListResponse> list() {
|
public ApiResponse<GameListResponse> list() {
|
||||||
GameListResponse result = gameService.getGameList();
|
GameListResponse result = gameService.getGameList();
|
||||||
@@ -43,7 +42,6 @@ public class GameController {
|
|||||||
* @param gameId 游戏 ID(必填)
|
* @param gameId 游戏 ID(必填)
|
||||||
* @param status 状态筛选(可选)
|
* @param status 状态筛选(可选)
|
||||||
*/
|
*/
|
||||||
@SaIgnore
|
|
||||||
@GetMapping("/room/list")
|
@GetMapping("/room/list")
|
||||||
public ApiResponse<RoomListResponse> roomList(
|
public ApiResponse<RoomListResponse> roomList(
|
||||||
@RequestParam String gameId,
|
@RequestParam String gameId,
|
||||||
@@ -59,7 +57,7 @@ public class GameController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/room/create")
|
@PostMapping("/room/create")
|
||||||
public ApiResponse<CreateRoomResponse> createRoom(@Valid @RequestBody CreateRoomRequest request) {
|
public ApiResponse<CreateRoomResponse> createRoom(@Valid @RequestBody CreateRoomRequest request) {
|
||||||
String userId = StpUtil.getLoginIdAsString();
|
String userId = authProvider.getCurrentUserId();
|
||||||
CreateRoomResponse result = gameService.createRoom(request, userId);
|
CreateRoomResponse result = gameService.createRoom(request, userId);
|
||||||
return ApiResponse.ok(result);
|
return ApiResponse.ok(result);
|
||||||
}
|
}
|
||||||
@@ -71,7 +69,7 @@ public class GameController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/room/join")
|
@PostMapping("/room/join")
|
||||||
public ApiResponse<RoomItemResponse> joinRoom(@Valid @RequestBody JoinRoomRequest request) {
|
public ApiResponse<RoomItemResponse> joinRoom(@Valid @RequestBody JoinRoomRequest request) {
|
||||||
String userId = StpUtil.getLoginIdAsString();
|
String userId = authProvider.getCurrentUserId();
|
||||||
RoomItemResponse result = gameService.joinRoom(request, userId);
|
RoomItemResponse result = gameService.joinRoom(request, userId);
|
||||||
return ApiResponse.ok(result);
|
return ApiResponse.ok(result);
|
||||||
}
|
}
|
||||||
@@ -83,7 +81,7 @@ public class GameController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/room/leave")
|
@PostMapping("/room/leave")
|
||||||
public ApiResponse<Void> leaveRoom(@Valid @RequestBody LeaveRoomRequest request) {
|
public ApiResponse<Void> leaveRoom(@Valid @RequestBody LeaveRoomRequest request) {
|
||||||
String userId = StpUtil.getLoginIdAsString();
|
String userId = authProvider.getCurrentUserId();
|
||||||
gameService.leaveRoom(request.roomId(), userId);
|
gameService.leaveRoom(request.roomId(), userId);
|
||||||
return ApiResponse.ok(null);
|
return ApiResponse.ok(null);
|
||||||
}
|
}
|
||||||
@@ -95,7 +93,7 @@ public class GameController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/room/kick")
|
@PostMapping("/room/kick")
|
||||||
public ApiResponse<Void> kickPlayer(@Valid @RequestBody KickPlayerRequest request) {
|
public ApiResponse<Void> kickPlayer(@Valid @RequestBody KickPlayerRequest request) {
|
||||||
String ownerId = StpUtil.getLoginIdAsString();
|
String ownerId = authProvider.getCurrentUserId();
|
||||||
gameService.kickPlayer(request.roomId(), ownerId, request.userId());
|
gameService.kickPlayer(request.roomId(), ownerId, request.userId());
|
||||||
return ApiResponse.ok(null);
|
return ApiResponse.ok(null);
|
||||||
}
|
}
|
||||||
@@ -107,7 +105,7 @@ public class GameController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/room/ready")
|
@PostMapping("/room/ready")
|
||||||
public ApiResponse<Boolean> toggleReady(@Valid @RequestBody ReadyRequest request) {
|
public ApiResponse<Boolean> toggleReady(@Valid @RequestBody ReadyRequest request) {
|
||||||
String userId = StpUtil.getLoginIdAsString();
|
String userId = authProvider.getCurrentUserId();
|
||||||
boolean ready = gameService.toggleReady(request.roomId(), userId);
|
boolean ready = gameService.toggleReady(request.roomId(), userId);
|
||||||
return ApiResponse.ok(ready);
|
return ApiResponse.ok(ready);
|
||||||
}
|
}
|
||||||
@@ -117,7 +115,6 @@ public class GameController {
|
|||||||
*
|
*
|
||||||
* 无需登录即可查看(观战者也可查看)。
|
* 无需登录即可查看(观战者也可查看)。
|
||||||
*/
|
*/
|
||||||
@SaIgnore
|
|
||||||
@GetMapping("/room/{roomId}")
|
@GetMapping("/room/{roomId}")
|
||||||
public ApiResponse<RoomDetailResponse> roomDetail(@PathVariable String roomId) {
|
public ApiResponse<RoomDetailResponse> roomDetail(@PathVariable String roomId) {
|
||||||
RoomDetailResponse result = gameService.getRoomDetail(roomId);
|
RoomDetailResponse result = gameService.getRoomDetail(roomId);
|
||||||
@@ -132,7 +129,7 @@ public class GameController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/room/start")
|
@PostMapping("/room/start")
|
||||||
public ApiResponse<Void> startGame(@Valid @RequestBody StartGameRequest request) {
|
public ApiResponse<Void> startGame(@Valid @RequestBody StartGameRequest request) {
|
||||||
String ownerId = StpUtil.getLoginIdAsString();
|
String ownerId = authProvider.getCurrentUserId();
|
||||||
gameService.startGame(request.roomId(), ownerId);
|
gameService.startGame(request.roomId(), ownerId);
|
||||||
return ApiResponse.ok(null);
|
return ApiResponse.ok(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.webgame.webgamebackend.controller;
|
package com.webgame.webgamebackend.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
|
||||||
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
||||||
import com.webgame.webgamebackend.common.dto.user.UpdateStatusRequest;
|
import com.webgame.webgamebackend.common.dto.user.UpdateStatusRequest;
|
||||||
import com.webgame.webgamebackend.common.dto.user.UpdateUserInfoRequest;
|
import com.webgame.webgamebackend.common.dto.user.UpdateUserInfoRequest;
|
||||||
@@ -22,6 +22,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final AuthenticationProvider authProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前登录用户信息
|
* 获取当前登录用户信息
|
||||||
@@ -39,7 +40,7 @@ public class UserController {
|
|||||||
*/
|
*/
|
||||||
@PutMapping("/info")
|
@PutMapping("/info")
|
||||||
public ApiResponse<UserInfoResponse> updateInfo(@Valid @RequestBody UpdateUserInfoRequest request) {
|
public ApiResponse<UserInfoResponse> updateInfo(@Valid @RequestBody UpdateUserInfoRequest request) {
|
||||||
String accountId = StpUtil.getLoginIdAsString();
|
String accountId = authProvider.getCurrentUserId();
|
||||||
UserInfoResponse result = userService.updateUserInfo(accountId, request);
|
UserInfoResponse result = userService.updateUserInfo(accountId, request);
|
||||||
return ApiResponse.ok(result);
|
return ApiResponse.ok(result);
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ public class UserController {
|
|||||||
*/
|
*/
|
||||||
@PatchMapping("/status")
|
@PatchMapping("/status")
|
||||||
public ApiResponse<Void> updateStatus(@Valid @RequestBody UpdateStatusRequest request) {
|
public ApiResponse<Void> updateStatus(@Valid @RequestBody UpdateStatusRequest request) {
|
||||||
String accountId = StpUtil.getLoginIdAsString();
|
String accountId = authProvider.getCurrentUserId();
|
||||||
userService.updateStatus(accountId, request);
|
userService.updateStatus(accountId, request);
|
||||||
return ApiResponse.ok(null);
|
return ApiResponse.ok(null);
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,7 @@ public class UserController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/avatar")
|
@PostMapping("/avatar")
|
||||||
public ApiResponse<UserInfoResponse> uploadAvatar(@RequestParam("file") MultipartFile file) {
|
public ApiResponse<UserInfoResponse> uploadAvatar(@RequestParam("file") MultipartFile file) {
|
||||||
String accountId = StpUtil.getLoginIdAsString();
|
String accountId = authProvider.getCurrentUserId();
|
||||||
UserInfoResponse result = userService.uploadAvatar(accountId, file);
|
UserInfoResponse result = userService.uploadAvatar(accountId, file);
|
||||||
return ApiResponse.ok(result);
|
return ApiResponse.ok(result);
|
||||||
}
|
}
|
||||||
@@ -76,7 +77,7 @@ public class UserController {
|
|||||||
*/
|
*/
|
||||||
@DeleteMapping("/avatar")
|
@DeleteMapping("/avatar")
|
||||||
public ApiResponse<UserInfoResponse> deleteAvatar() {
|
public ApiResponse<UserInfoResponse> deleteAvatar() {
|
||||||
String accountId = StpUtil.getLoginIdAsString();
|
String accountId = authProvider.getCurrentUserId();
|
||||||
UserInfoResponse result = userService.deleteAvatar(accountId);
|
UserInfoResponse result = userService.deleteAvatar(accountId);
|
||||||
return ApiResponse.ok(result);
|
return ApiResponse.ok(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.webgame.webgamebackend.service.impl;
|
package com.webgame.webgamebackend.service.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
|
||||||
import com.webgame.webgamebackend.common.constants.UserConstants;
|
import com.webgame.webgamebackend.common.constants.UserConstants;
|
||||||
import com.webgame.webgamebackend.common.dto.account.LoginRequest;
|
import com.webgame.webgamebackend.common.dto.account.LoginRequest;
|
||||||
import com.webgame.webgamebackend.common.dto.account.LoginResponse;
|
import com.webgame.webgamebackend.common.dto.account.LoginResponse;
|
||||||
@@ -33,6 +33,7 @@ public class AccountServiceImpl implements AccountService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final BCryptPasswordEncoder passwordEncoder;
|
private final BCryptPasswordEncoder passwordEncoder;
|
||||||
private final RefreshTokenService refreshTokenService;
|
private final RefreshTokenService refreshTokenService;
|
||||||
|
private final AuthenticationProvider authProvider;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoginResponse login(LoginRequest request) {
|
public LoginResponse login(LoginRequest request) {
|
||||||
@@ -41,9 +42,8 @@ public class AccountServiceImpl implements AccountService {
|
|||||||
if (!passwordEncoder.matches(request.password(), account.getPassword())) {
|
if (!passwordEncoder.matches(request.password(), account.getPassword())) {
|
||||||
throw new BusinessException(ErrorCode.BAD_CREDENTIALS);
|
throw new BusinessException(ErrorCode.BAD_CREDENTIALS);
|
||||||
}
|
}
|
||||||
// Sa-Token 登录,生成 access token
|
// 登录,生成 access token
|
||||||
StpUtil.login(account.getId());
|
String accessToken = authProvider.login(account.getId());
|
||||||
String accessToken = StpUtil.getTokenValue();
|
|
||||||
// 生成 refresh token
|
// 生成 refresh token
|
||||||
String refreshToken = refreshTokenService.create(account.getId());
|
String refreshToken = refreshTokenService.create(account.getId());
|
||||||
log.info("用户登录成功: username={}", request.username());
|
log.info("用户登录成功: username={}", request.username());
|
||||||
@@ -75,8 +75,7 @@ public class AccountServiceImpl implements AccountService {
|
|||||||
|
|
||||||
log.info("用户注册成功: username={}", request.username());
|
log.info("用户注册成功: username={}", request.username());
|
||||||
// 注册后自动登录,生成 access token 和 refresh token
|
// 注册后自动登录,生成 access token 和 refresh token
|
||||||
StpUtil.login(account.getId());
|
String accessToken = authProvider.login(account.getId());
|
||||||
String accessToken = StpUtil.getTokenValue();
|
|
||||||
String refreshToken = refreshTokenService.create(account.getId());
|
String refreshToken = refreshTokenService.create(account.getId());
|
||||||
return new LoginResponse(accessToken, refreshToken);
|
return new LoginResponse(accessToken, refreshToken);
|
||||||
}
|
}
|
||||||
@@ -85,8 +84,7 @@ public class AccountServiceImpl implements AccountService {
|
|||||||
public LoginResponse refresh(String refreshToken) {
|
public LoginResponse refresh(String refreshToken) {
|
||||||
// 校验 refresh token,有效则用它签发新的 access token
|
// 校验 refresh token,有效则用它签发新的 access token
|
||||||
String userId = refreshTokenService.validate(refreshToken);
|
String userId = refreshTokenService.validate(refreshToken);
|
||||||
StpUtil.login(userId);
|
String accessToken = authProvider.login(userId);
|
||||||
String accessToken = StpUtil.getTokenValue();
|
|
||||||
log.info("令牌刷新成功: userId={}", userId);
|
log.info("令牌刷新成功: userId={}", userId);
|
||||||
// refresh token 保持不变,只返回新的 access token
|
// refresh token 保持不变,只返回新的 access token
|
||||||
return new LoginResponse(accessToken, refreshToken);
|
return new LoginResponse(accessToken, refreshToken);
|
||||||
@@ -95,9 +93,9 @@ public class AccountServiceImpl implements AccountService {
|
|||||||
@Override
|
@Override
|
||||||
public void logout(String refreshToken) {
|
public void logout(String refreshToken) {
|
||||||
// 获取当前登录用户ID(用于日志)
|
// 获取当前登录用户ID(用于日志)
|
||||||
String loginId = (String) StpUtil.getLoginId();
|
String loginId = authProvider.getCurrentUserId();
|
||||||
// 登出 Sa-Token,使 access token 失效
|
// 登出,使 access token 失效
|
||||||
StpUtil.logout();
|
authProvider.logout();
|
||||||
// 撤销 refresh token,使其在 Redis 中删除
|
// 撤销 refresh token,使其在 Redis 中删除
|
||||||
refreshTokenService.revoke(refreshToken);
|
refreshTokenService.revoke(refreshToken);
|
||||||
log.info("用户已登出: userId={}", loginId);
|
log.info("用户已登出: userId={}", loginId);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.webgame.webgamebackend.service.impl;
|
package com.webgame.webgamebackend.service.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
|
||||||
import com.webgame.webgamebackend.common.dto.user.UpdateStatusRequest;
|
import com.webgame.webgamebackend.common.dto.user.UpdateStatusRequest;
|
||||||
import com.webgame.webgamebackend.common.dto.user.UpdateUserInfoRequest;
|
import com.webgame.webgamebackend.common.dto.user.UpdateUserInfoRequest;
|
||||||
import com.webgame.webgamebackend.common.dto.user.UserInfoResponse;
|
import com.webgame.webgamebackend.common.dto.user.UserInfoResponse;
|
||||||
@@ -48,10 +48,11 @@ public class UserServiceImpl implements UserService {
|
|||||||
private static final long MAX_AVATAR_SIZE = 2 * 1024 * 1024;
|
private static final long MAX_AVATAR_SIZE = 2 * 1024 * 1024;
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final AuthenticationProvider authProvider;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserInfoResponse getCurrentUserInfo() {
|
public UserInfoResponse getCurrentUserInfo() {
|
||||||
String accountId = StpUtil.getLoginIdAsString();
|
String accountId = authProvider.getCurrentUserId();
|
||||||
log.info("[用户服务] 获取当前登录用户信息, accountId: {}", accountId);
|
log.info("[用户服务] 获取当前登录用户信息, accountId: {}", accountId);
|
||||||
UserEntity user = userRepository.findByAccountId(accountId)
|
UserEntity user = userRepository.findByAccountId(accountId)
|
||||||
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
|
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.webgame.webgamebackend.sse;
|
package com.webgame.webgamebackend.sse;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -29,29 +29,28 @@ public class SseController {
|
|||||||
private static final long SSE_TIMEOUT_MS = 30 * 60 * 1000L;
|
private static final long SSE_TIMEOUT_MS = 30 * 60 * 1000L;
|
||||||
|
|
||||||
private final SseConnectionManager connectionManager;
|
private final SseConnectionManager connectionManager;
|
||||||
|
private final AuthenticationProvider authProvider;
|
||||||
|
|
||||||
@GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
@GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||||
public SseEmitter subscribe(
|
public SseEmitter subscribe(
|
||||||
@RequestHeader(value = "Last-Event-ID", required = false) String lastEventId,
|
@RequestHeader(value = "Last-Event-ID", required = false) String lastEventId,
|
||||||
HttpServletResponse response) throws IOException {
|
HttpServletResponse response) throws IOException {
|
||||||
|
|
||||||
// SSE 路径已排除在 SaInterceptor 之外,此处手动校验登录态
|
// SSE 路径已排除在 AuthInterceptor 之外,此处手动校验登录态
|
||||||
String token = StpUtil.getTokenValue();
|
String token = authProvider.getCurrentToken();
|
||||||
if (token == null || token.isBlank()) {
|
if (token == null || token.isBlank()) {
|
||||||
log.warn("[SSE] 缺少认证 token");
|
log.warn("[SSE] 缺少认证 token");
|
||||||
return writeAuthError(response, HttpServletResponse.SC_UNAUTHORIZED, "缺少认证信息,请先登录");
|
return writeAuthError(response, HttpServletResponse.SC_UNAUTHORIZED, "缺少认证信息,请先登录");
|
||||||
}
|
}
|
||||||
|
|
||||||
Object loginId = StpUtil.getLoginIdByToken(token);
|
String userId = authProvider.getUserIdByToken(token);
|
||||||
if (loginId == null) {
|
if (userId == null) {
|
||||||
log.warn("[SSE] token 无效或已过期");
|
log.warn("[SSE] token 无效或已过期");
|
||||||
return writeAuthError(response, HttpServletResponse.SC_UNAUTHORIZED, "登录已过期,请重新登录");
|
return writeAuthError(response, HttpServletResponse.SC_UNAUTHORIZED, "登录已过期,请重新登录");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置 Sa-Token 上下文,后续 StpUtil.getLoginIdAsString() 可用
|
// 设置认证上下文,后续 getCurrentUserId() 可用
|
||||||
StpUtil.setTokenValue(token, -1);
|
authProvider.setContextToken(token, -1);
|
||||||
|
|
||||||
String userId = loginId.toString();
|
|
||||||
SseEmitter emitter = new SseEmitter(SSE_TIMEOUT_MS);
|
SseEmitter emitter = new SseEmitter(SSE_TIMEOUT_MS);
|
||||||
String connectionId = connectionManager.register(userId, emitter);
|
String connectionId = connectionManager.register(userId, emitter);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.webgame.webgamebackend.ws;
|
package com.webgame.webgamebackend.ws;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import com.webgame.webgamebackend.common.auth.AuthenticationProvider;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.webgame.webgamebackend.service.GomokuGameService;
|
import com.webgame.webgamebackend.service.GomokuGameService;
|
||||||
@@ -35,6 +35,7 @@ public class RoomWebSocketHandler extends TextWebSocketHandler {
|
|||||||
private final RoomSessionManager sessionManager;
|
private final RoomSessionManager sessionManager;
|
||||||
private final GomokuGameService gomokuGameService;
|
private final GomokuGameService gomokuGameService;
|
||||||
private final WsMessageRouter router;
|
private final WsMessageRouter router;
|
||||||
|
private final AuthenticationProvider authProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册所有消息类型与对应处理器
|
* 注册所有消息类型与对应处理器
|
||||||
@@ -109,12 +110,11 @@ public class RoomWebSocketHandler extends TextWebSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 通过 token 获取用户 ID
|
// 通过 token 获取用户 ID
|
||||||
Object loginId = StpUtil.getLoginIdByToken(token);
|
String userId = authProvider.getUserIdByToken(token);
|
||||||
if (loginId == null) {
|
if (userId == null) {
|
||||||
session.close(CloseStatus.POLICY_VIOLATION);
|
session.close(CloseStatus.POLICY_VIOLATION);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String userId = loginId.toString();
|
|
||||||
|
|
||||||
// 注册会话(内部会清理同用户的旧会话)
|
// 注册会话(内部会清理同用户的旧会话)
|
||||||
sessionManager.addSession(roomId, userId, session);
|
sessionManager.addSession(roomId, userId, session);
|
||||||
|
|||||||
Reference in New Issue
Block a user