aspect实现请求校验,但是WebSocket 端点类不能被 AOP 代理解决方案

冲突点:切面配置的扫描范围(Pointcut)太广了,例如 AuthAspectexecution(* com.xxx.controller....(...))。

发生了什么:WebSocket 类()放在了 包下。Spring AOP 扫描到了它,并试图给它创建一个 DocChatWebSocketControllercontrollerCGLIB 代理(Proxy)来实现拦截。

为何报错:'@ServerEnd 是 Java EE 标准(JSR 356),Spring 的 @ServerEndpointServerEndpointExporter 在注册端点时,需要直接操作。当它发现这个 Bean 变成了一个 原生类Spring AOP 代理对象时,它无法读取原生类上的 注解元数据,从而抛出 。@ServerEndpointIllegalStateException

简单来说:WebSocket 端点类不能被 AOP 代理,必须保持"纯净"。

✅ 解决方案

有两个方案,推荐 (最快)或 (架构更合理)。方案一方案二

dart 复制代码
<!-- Spring Boot AOP starter(核心依赖,包含AspectJ相关组件) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <!-- 如果你的项目已经指定了spring-boot-starter-parent的版本,这里无需写version -->
    <!-- 若未指定,需添加版本,比如:<version>2.7.15</version>(根据你的Spring Boot版本调整) -->
</dependency>

方案一:修改 AOP 切面,排除 WebSocket 类(推荐)

在切点AuthAspect@ServerEndpoint 注解的类。

修改 :AuthAspect.java

java 复制代码
package com.yige.security;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
// 引入 WebSocket 注解
import javax.websocket.server.ServerEndpoint; 

@Aspect
@Component
public class AuthAspect {

    // 原来的切点可能长这样:
    // @Pointcut("execution(public * com.iflytek.knowledge.controller..*.*(..))")
    
    // 👇 修改后的切点:增加 !@within(...)
    @Pointcut("execution(public * com.iflytek.knowledge.controller..*.*(..)) " +
            "&& !execution(public * com.iflytek.knowledge.controller..*.login(..))" + 
            "&& !@within(javax.websocket.server.ServerEndpoint)") // 关键:排除带有 @ServerEndpoint 注解的类
    public void authPointCut() {}

    @Around("authPointCut()")
    public Object interceptor(ProceedingJoinPoint point) throws Throwable {
        // ... 你的原有逻辑 ...
        return point.proceed();
    }
}

原理:'!@within(javax.websocket.server.ServerE 告诉 Spring AOP:"只要这个类头上戴了 !@within(javax.websocket.server.ServerEndpoint)@ServerEndpoint 的帽子,你就别去碰它,别生成代理。"

方案二:调整包结构(物理隔离)

既然 WebSocket 不是 HTTP Controller,从架构整洁度来说,不应该放在 包下。controller

新建包:com.iflytek.knowledge.websocket

移动文件:把 移到这个新包里。DocChatWebSocketController

效果:因为你的 AOP 只扫 包,自然就扫不到 包了,问题解决。controllerwebsocket

⚠️ 重要的后续问题:WebSocket 的鉴权怎么办?

一旦你排除了 AOP,,变成了裸奔状态。WebSocket 接口就失去了"自动拦截验证"的保护

WebSocket 的握手和 HTTP 不同,且 'A 里的 AuthAspectHttpServletRequest request = attributes.getRequest(); 在 WebSocket 的 方法里通常是拿不到东西的,或者逻辑不通。@OnMessage

你需要在 WebSocket 的 方法里手动调用 进行验证。@OnOpenSessionManager

WebSocket 鉴权最佳实践:

前端连接时,把 Token 放在 URL 参数里:ws://localhost:8080/ws/chat?token=YOUR_TOKEN

后端代码调整 ():DocChatWebSocketController

Java 复制代码
@ServerEndpoint(value = "/ws/chat")
@Component
public class DocChatWebSocketController {

    // 注意:WebSocket 是多例的,这里注入 Bean 需要特殊处理,或者用 static 上下文获取
    // 这里假设你已经解决了 WebSocket 注入 Spring Bean 的问题(通常用 setApplicationContext)
    private static SessionManager sessionManager; 

    @Autowired
    public void setSessionManager(SessionManager manager) {
        DocChatWebSocketController.sessionManager = manager;
    }

    @OnOpen
    public void onOpen(Session session) throws IOException {
        // 1. 从 URL 查询参数中获取 Token
        String queryString = session.getQueryString(); // 获取 "token=xxxx"
        String token = getTokenFromQuery(queryString);

        try {
            // 2. 手动调用鉴权逻辑!
            if (token == null) {
                throw new Exception("无 Token");
            }
            // 这里调用我们之前写的 SessionManager
            sessionManager.validate(token); 
            
            System.out.println("WebSocket 连接成功,鉴权通过");

        } catch (Exception e) {
            // 3. 鉴权失败,直接关闭连接
            System.err.println("WebSocket 鉴权失败: " + e.getMessage());
            session.close(); // 拒绝连接
        }
    }

    // 简单的解析工具方法
    private String getTokenFromQuery(String queryString) {
        if (queryString != null && queryString.contains("token=")) {
            String[] params = queryString.split("&");
            for (String param : params) {
                if (param.startsWith("token=")) {
                    return param.split("=")[1];
                }
            }
        }
        return null;
    }
}

总结

立即解决启动报错:在 AuthAspect 的 @Pointcut 中加上 && !@within(javax.websocket.server.ServerEndpoint)。

补全安全漏洞:在 WebSocket 的 @OnOpen 方法中,手动调用 SessionManager.validate(token),因为 AOP 不再保护它了。

相关推荐
sunoo-22914 分钟前
【网络编程 + 数据库】select 与 epoll 核心区别详解 + SQLite 入门到 C 接口全攻略
linux·网络·数据库·vscode·学习·sqlite
长脖鹿Johnny17 分钟前
游戏输入系统框架设计(三):网络输入权威与可验证性
网络·游戏·游戏开发·架构设计·输入系统·网络同步
Mortalbreeze27 分钟前
深入理解 Linux IO 模型(二):多路复用——select
linux·运维·服务器·网络·tcp/ip
SamChan9040 分钟前
PDF翻译服务端到端基准测试:4款主流方案的吞吐量、延迟、内存占用对比
服务器·网络·python·ai·pdf·wpf
Sagittarius_A*41 分钟前
CVE-2026-42271:从 MCP stdio 测试接口看 LiteLLM 的命令执行风险与防御闭环
网络·windows·安全·cve·rce
DLYSB_1 小时前
旧上位机不肯改怎么办:原生 TCP 字节帧接入声光语音终端的改造实录
网络·网络协议·tcp/ip·报警灯
ACP广源盛139246256731 小时前
M6/M5 Pro Mac mini 端侧 AI 爆发@ACP#YLB3118 存储扩展芯片在本地 AI 服务中的机会与落地场景
大数据·网络·数据库·人工智能·嵌入式硬件·macos
程序员夏洛1 小时前
HTTP 2.0 和 3.0 有什么区别?
网络·网络协议·http
丶浅行DE时光1 小时前
管道式电磁流量计选型指南 介质腐蚀与工况适配方案推荐
大数据·网络·人工智能·科技·推荐算法
段一凡-华北理工大学2 小时前
高炉智能布料技术与炉料分布优化~系列文章03:布料矩阵解码:环位、角度、圈数与料流的量化控制
java·网络·人工智能·矩阵·高炉智能化·高炉布料矩阵优化·高炉智能布料技术