Spring Boot与Angular 4集成时的CORS Origin问题排查
Hey there, let's tackle your CORS issue head-on. You're right to suspect the JWTAuthorizationFiler—the way you're handling CORS headers directly in the filter is conflicting with Spring's native CORS processing, especially for preflight OPTIONS requests. Here's a step-by-step solution to get things working:
1. Replace Manual CORS Header Handling with Spring's Official Configuration
Spring has a built-in, robust way to configure CORS that avoids conflicts with security filters. Add this bean to your SecurityConfig class:
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.CorsConfigurationSource; import java.util.Arrays; @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // Replace with your Angular app's actual URL (avoid "*" if using credentials) configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList( "Origin", "Accept", "X-Requested-With", "Content-Type", "Access-Control-Request-Method", "Access-Control-Request-Headers", "Authorization" // Match your JWT header name )); // Expose the Authorization header so Angular can access it after login configuration.setExposedHeaders(Arrays.asList("Authorization")); // Enable this if your Angular app uses withCredentials: true in requests configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
Then update your configure(HttpSecurity http) method to use this CORS configuration instead of manual headers:
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .cors().configurationSource(corsConfigurationSource()) // Add this line .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/login/**", "/register/**", "/paramsApi/**").permitAll() .antMatchers(HttpMethod.POST, "/studentResource/**").hasAuthority("ADMIN") .antMatchers(HttpMethod.GET, "/studentResource/**").hasAuthority("ADMIN") .anyRequest().authenticated() .and() .addFilter(new JWTAuthenticationFilter(authenticationManager())) .addFilterBefore(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class); // Fix class name spelling here }
2. Clean Up the JWT Authorization Filter
Remove all manual CORS header code from your JWTAuthorizationFilter (fixed the class name spelling too) and add error handling for invalid JWTs:
public class JWTAuthorizationFilter extends OncePerRequestFilter { @SuppressWarnings("unchecked") @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Let Spring's CORS config handle headers now if (request.getMethod().equals(HttpMethod.OPTIONS.name())) { response.setStatus(HttpServletResponse.SC_OK); filterChain.doFilter(request, response); return; } String jwt = request.getHeader(SecurityConstants.HEADER_STRING); if (jwt == null || !jwt.startsWith(SecurityConstants.TOKEN_PREFIX)) { filterChain.doFilter(request, response); return; } try { Claims claims = Jwts.parser() .setSigningKey(SecurityConstants.SECRET) .parseClaimsJws(jwt.replace(SecurityConstants.TOKEN_PREFIX, "")) .getBody(); String username = claims.getSubject(); ArrayList<Map<String, String>> roles = (ArrayList<Map<String, String>>) claims.get("roles"); Collection<GrantedAuthority> authorities = new ArrayList<>(); roles.forEach(r -> authorities.add(new SimpleGrantedAuthority(r.get("authority")))); UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( username, null, authorities ); SecurityContextHolder.getContext().setAuthentication(authToken); } catch (Exception e) { // Handle invalid/expired JWTs gracefully response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid or expired JWT token"); return; } filterChain.doFilter(request, response); } }
3. Verify Angular Request Configuration
Make sure your Angular login request includes withCredentials: true if you're using cookies or authenticated sessions (match the setAllowCredentials(true) in your Spring config):
this.http.post('/login', yourCredentials, { withCredentials: true }) .subscribe(response => { // Extract and store the JWT from the Authorization header });
Why This Works
- Spring's native CORS configuration properly handles preflight OPTIONS requests, which your manual filter code was partially handling but not consistently.
- Separating CORS logic from the JWT filter avoids header conflicts and ensures all request types (including preflight) get the correct headers.
- Adding error handling for invalid JWTs prevents unexpected 500 errors and gives clear feedback to the frontend.
内容的提问来源于stack exchange,提问作者Soufiane Rabii

