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 不再保护它了。

相关推荐
老董杂货铺13 小时前
安防视频互联网化利器:EasyNVR全面解析
网络·信息可视化·音视频
学嵌入式的小杨同学13 小时前
【嵌入式 C 语言实战】栈、队列、二叉树核心解析:存储原理 + 应用场景 + 实现思路
linux·c语言·网络·数据结构·数据库·后端·spring
舰长11513 小时前
SSLITLS协议信息泄露漏洞(CVE-2016-2183)【原理扫描】
网络
智算菩萨14 小时前
互联网的神经网络:OSI七层分层模型深度讲解
网络
小宇的天下14 小时前
Calibre :Standard Verification Rule Format(SVRF) Manual (1-1)
大数据·前端·网络
九成宫14 小时前
计算机网络期末复习——第5章:链路层 Part One
网络·笔记·计算机网络·软件工程
多多*14 小时前
计算机网络相关 讲一下rpc与传统http的区别
java·开发语言·网络·jvm·c#
xwz小王子14 小时前
Nature Electronics 新加坡国立大学研发了基于柔性拓扑结构服装的体感传感器网络
网络·体感传感器
独行soc14 小时前
2026年渗透测试面试题总结-5(题目+回答)
android·网络·python·安全·web安全·渗透测试
希赛网14 小时前
网工面试:常问技术问题汇总(3)
服务器·前端·网络·网络工程师·ospf·网工面试·技术面