I have a problem with CORS error in my Angluar 8 app.
I receive response from server with status 200 and valid JWT token which, I think, means server is properly configured, but still got error in console:
Access to XMLHttpRequest at 'http://localhost:8080/login' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The Angular app is running locally on my machine on port 4200
The server is my own Spring Boot app with Security, running also locally on port 8080
I have implemented custom Security Filter to handle CORS:
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "180");
filterChain.doFilter(servletRequest, servletResponse);
}
}
And added in Security config:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final RestAuthenticationSuccessHandler authenticationSuccessHandler;
private final RestAuthenticationFailureHandler authenticationFailureHandler;
private final CustomUserDetailsService userDetailsService;
private final String secret;
private final TenantFilter tenantFilter;
private final CorsFilter corsFilter;
private final UserTenantRelationRepository userTenantRelationRepository;
@Autowired
public SecurityConfig(RestAuthenticationSuccessHandler authenticationSuccessHandler,
RestAuthenticationFailureHandler authenticationFailureHandler,
CustomUserDetailsService userDetailsService,
@Value("${jwt.secret}") String secret, TenantFilter tenantFilter, CorsFilter corsFilter, UserTenantRelationRepository userTenantRelationRepository) {
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authenticationFailureHandler = authenticationFailureHandler;
this.userDetailsService = userDetailsService;
this.secret = secret;
this.tenantFilter = tenantFilter;
this.corsFilter = corsFilter;
this.userTenantRelationRepository = userTenantRelationRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter, SessionManagementFilter.class)
.addFilterBefore(tenantFilter, BasicAuthenticationFilter.class)
.csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/register").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(authenticationFilter(userTenantRelationRepository))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userDetailsService, secret))
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
As I mentioned before, I receive response 200 and valid JWT token, so I am very confused about it.
EDIT
Ok, so I had read about preflight request that you mentioned and fixed my filter to handle this request.
Now it looks like this:
@Component
public class CorsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token");
response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
}
}
And configuration is now:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter, ChannelProcessingFilter.class)
.addFilterBefore(tenantFilter, BasicAuthenticationFilter.class)
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/register").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(authenticationFilter(userTenantRelationRepository))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userDetailsService, secret))
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
Thank you all for your tips.