You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用EventSource建立SSE连接时遭遇403错误的解决求助

问题描述

使用SSE协议实现后端向前端推送数据时遭遇403错误,已确认JWT Token有效,相关代码如下:

后端(Spring Security JWT)

@CrossOrigin(origins = "http://localhost:4444", allowCredentials = "true")
@GetMapping("/")
public Flux<ServerSentEvent<List<UserResponse>>> getAll() throws CancelQueryException {
    return Flux.interval(Duration.ofSeconds(3))
            .flatMap(sequence -> {
                List<UserResponse> data = null;
                try {
                    data = userService.getAllUsers();
                    System.out.println("data: " + data);
                } catch (CancelQueryException e) {
                    throw new RuntimeException(e);
                }
                return Mono.just(ServerSentEvent.<List<UserResponse>>builder()
                        .id(String.valueOf(sequence))
                        .event("update")
                        .data(data)
                        .build());
            });
}

前端(React)

useEffect(() => {
    const token =
      "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJld2ZhZXJnZSIsImlhdCI6MTY4ODEwNTg4MCwiZXhwIjoxNjg4NzEwNjgwfQ.V3d_1n7QElQZpPshH_UkkR707sLJlxqz7fYmqSJ3V-4";
    const config = {
      withCredentials: true,
      headers: {
        Authorization: `Bearer ${token}`,
      },
    };
    const url = "http://localhost:8558/monitoring-location/api/v1/admin/user/";
    try {
      const eventSource = new EventSource(url, config);

      eventSource.onmessage = (event) => {
        const receivedEvent = JSON.parse(event.data);
        console.log(receivedEvent);
      };
      console.log("running");

      return () => {
        console.log("close connecttion");
        eventSource.close(); // Đóng kết nối SSE khi component bị hủy
      };
    } catch (error) {
      console.log("error ----", error);
    }
  }, []);

排查与解决步骤

1. 修复EventSource自定义请求头问题

原生EventSourceAPI 不支持携带自定义请求头,你代码里的Authorization头不会被发送到后端,这是导致403的核心原因——Spring Security没收到Token,直接拦截请求。

有两种解决方案:

  • 方案一:URL参数携带Token
    修改前端URL:
const url = `http://localhost:8558/monitoring-location/api/v1/admin/user/?token=${token}`;

后端配置Spring Security支持从请求参数提取Token,比如在JWT过滤器中添加参数提取逻辑:

// 示例:在JwtAuthenticationFilter中添加参数提取
String token = request.getParameter("token");
if (token != null && token.startsWith("Bearer ")) {
    token = token.substring(7);
}
// 后续验证token逻辑
  • 方案二:使用Polyfill支持自定义头
    安装eventsource-polyfill依赖:
npm install eventsource-polyfill

修改前端代码:

import EventSource from 'eventsource-polyfill';

// ... 原有代码
const eventSource = new EventSource(url, {
  headers: {
    'Authorization': `Bearer ${token}`
  },
  withCredentials: true
});

2. 验证Spring Security端点权限配置

确保SSE端点的权限规则和Token中的权限匹配:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            // 匹配你的SSE端点路径
            .antMatchers("/monitoring-location/api/v1/admin/user/**")
            // 确保Token包含对应角色/权限
            .hasAuthority("ROLE_ADMIN")
            .and()
            .oauth2ResourceServer()
            .jwt();
        // 其他配置...
    }
}

用JWT解析工具检查Token的authorities字段,确认包含端点要求的权限。

3. 统一CORS配置避免冲突

后端的@CrossOrigin注解可能和Spring Security的CORS配置冲突,建议使用全局CORS配置:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and() // 启用全局CORS
            .authorizeRequests()
            // ... 其他规则
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(Arrays.asList("http://localhost:4444"));
        config.setAllowedMethods(Arrays.asList("GET", "OPTIONS"));
        config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
        config.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

配置后可以去掉接口上的@CrossOrigin注解。

4. 再次验证Token有效性

用JWT解析工具检查:

  • Token的exp字段是否未过期
  • Token的签名密钥和后端配置的一致

内容的提问来源于stack exchange,提问作者Sơn Giang Lê Quốc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 13:53:12