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

SpringBoot+Auth0跨域问题:上传视频时出现401 XHR错误

解决方案

1. 调整SecurityFilterChain过滤器顺序,确保CORS优先处理

Spring Security过滤器链顺序直接影响请求处理逻辑,CORS配置必须在认证逻辑之前执行。修改SecurityConfig.java的filterChain方法:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 优先配置CORS
        .csrf(csrf -> csrf.disable()) // 前后端分离场景通常禁用CSRF,按需配置令牌
        .authorizeHttpRequests(auth -> auth
            .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 允许OPTIONS预检请求
            .requestMatchers("/api/auth/**").permitAll()
            .requestMatchers("/api/videos/**").authenticated()
            .anyRequest().authenticated()
        )
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // JWT场景设置无状态
        .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 确保认证过滤器位置正确
    return http.build();
}

2. 完善CorsConfigurationSource配置,开启凭证支持

确保CORS配置覆盖上传请求所需的方法、请求头,并且允许携带认证凭证:

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(Collections.singletonList("http://localhost:4200")); // 替换为你的Angular部署地址
    config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
    config.setAllowCredentials(true); // 关键:允许携带Authorization头或Cookie
    config.setMaxAge(3600L); // 缓存预检请求结果,减少OPTIONS请求次数

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    return source;
}

3. 修复Angular端上传请求的凭证设置

确保前端请求携带认证令牌,同时开启withCredentials与后端配置对应:

uploadVideo(formData: FormData): Observable<any> {
    const authToken = localStorage.getItem('authToken');
    const headers = new HttpHeaders({
        'Authorization': `Bearer ${authToken}`
    });

    return this.http.post('/api/videos', formData, {
        headers: headers,
        withCredentials: true
    });
}

若使用全局拦截器统一添加认证头,需确保拦截器覆盖上传请求路径,且请求配置包含withCredentials。

4. 验证认证过滤器的有效性

检查JWT或自定义认证过滤器是否正确解析令牌并设置SecurityContext:

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader("Authorization");
        logger.info("Received Authorization header: {}", authHeader); // 日志排查令牌是否正常传递

        if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            try {
                Long userId = jwtTokenProvider.extractUserId(token);
                UserDetails userDetails = userDetailsService.loadUserById(userId);
                
                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities()
                );
                auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(auth);
            } catch (Exception e) {
                logger.error("Failed to set user authentication: {}", e.getMessage());
            }
        }

        filterChain.doFilter(request, response);
    }
}

确认过滤器已通过addFilterBefore正确添加到SecurityFilterChain,位置在UsernamePasswordAuthenticationFilter之前。

5. 排查OPTIONS预检请求

浏览器发送跨域POST前会先发OPTIONS预检请求,该请求不会携带认证头,必须允许匿名访问。确保authorizeHttpRequests中添加了OPTIONS请求的放行规则(如步骤1中的配置)。

内容的提问来源于stack exchange,提问作者Pablo Costa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 01:05:36