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
相关产品推荐
相关产品推荐

