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

相关推荐
数字护盾(和中)2 小时前
从边界突破到物理破坏:APT 工控攻击链路与防御闭环
网络
Saniffer_SH3 小时前
【每日一题】PCIe答疑 - 接大量 GPU 时主板不认设备或无法启动和MMIO的可能关系?
运维·服务器·网络·人工智能·驱动开发·fpga开发·硬件工程
大白的编程日记.3 小时前
【计算网络学习笔记】Socket编程UDP实现简单聊天室
网络·笔记·学习
织元Zmetaboard3 小时前
什么是态势感知大屏?
网络·数据库
Web3VentureView3 小时前
培养全球Web3人才:SYNBO商学院正式启动运营
网络·金融·重构·web3·区块链
Saniffer_SH3 小时前
【每日一题】讲讲PCIe链路训练和枚举的前后关系
运维·服务器·网络·数据库·驱动开发·fpga开发·硬件工程
真正的醒悟3 小时前
图解网络35
开发语言·网络·php
老蒋新思维3 小时前
创客匠人峰会新视角:AI 时代知识变现的 “组织化转型”—— 从个人 IP 到 “AI+IP” 组织的增长革命
大数据·人工智能·网络协议·tcp/ip·创始人ip·创客匠人·知识变现
bleach-4 小时前
内网渗透之横向移动&持久化远程控制篇——利用ipc、sc、schtasks、AT,远程连接的winrm,wmic的使用和定时任务的创建
网络·windows·安全·web安全·网络安全·系统安全·安全威胁分析