Access blocked by CORS policy: Response to preflight request doesn't pass access control check

179,011

Solution 1

You may need to config the CORS at Spring Boot side. Please add below class in your Project.

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebConfig implements Filter,WebMvcConfigurer {



    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
      HttpServletResponse response = (HttpServletResponse) res;
      HttpServletRequest request = (HttpServletRequest) req;
      System.out.println("WebConfig; "+request.getRequestURI());
      response.setHeader("Access-Control-Allow-Origin", "*");
      response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
      response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With,observe");
      response.setHeader("Access-Control-Max-Age", "3600");
      response.setHeader("Access-Control-Allow-Credentials", "true");
      response.setHeader("Access-Control-Expose-Headers", "Authorization");
      response.addHeader("Access-Control-Expose-Headers", "responseType");
      response.addHeader("Access-Control-Expose-Headers", "observe");
      System.out.println("Request Method: "+request.getMethod());
      if (!(request.getMethod().equalsIgnoreCase("OPTIONS"))) {
          try {
              chain.doFilter(req, res);
          } catch(Exception e) {
              e.printStackTrace();
          }
      } else {
          System.out.println("Pre-flight");
          response.setHeader("Access-Control-Allow-Origin", "*");
          response.setHeader("Access-Control-Allow-Methods", "POST,GET,DELETE,PUT");
          response.setHeader("Access-Control-Max-Age", "3600");
          response.setHeader("Access-Control-Allow-Headers", "Access-Control-Expose-Headers"+"Authorization, content-type," +
          "USERID"+"ROLE"+
                  "access-control-request-headers,access-control-request-method,accept,origin,authorization,x-requested-with,responseType,observe");
          response.setStatus(HttpServletResponse.SC_OK);
      }

    }

}

UPDATE:

To append Token to each request you can create one Interceptor as below.

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = window.localStorage.getItem('tokenKey'); // you probably want to store it in localStorage or something


    if (!token) {
      return next.handle(req);
    }

    const req1 = req.clone({
      headers: req.headers.set('Authorization', `${token}`),
    });

    return next.handle(req1);
  }

}

Example

Solution 2

If you are using Spring as Back-End server and especially using Spring Security then i found a solution by putting http.cors(); in the configure method. The method looks like that:

protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests() // authorize
                .anyRequest().authenticated() // all requests are authenticated
                .and()
                .httpBasic();

        http.cors();
}

Solution 3

Since the originating port 4200 is different than 8080,So before angular sends a create (PUT) request,it will send an OPTIONS request to the server to check what all methods and what all access-controls are in place. Server has to respond to that OPTIONS request with list of allowed methods and allowed origins.

Since you are using spring boot, the simple solution is to add ".allowedOrigins("http://localhost:4200");"

In your spring config,class

@Configuration
@EnableWebMvc
public class SpringConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("http://localhost:4200");
    }
}

However a better approach will be to write a Filter(interceptor) which adds the necessary headers to each response.

Solution 4

CORS headers should be sent from the server. If you use PHP it will be like this:

header('Access-Control-Allow-Origin: your-host');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: your-methods like POST,GET');
header('Access-Control-Allow-Headers: content-type or other');
header('Content-Type: application/json');

Solution 5

You can just create the required CORS configuration as a bean. As per the code below this will allow all requests coming from any origin. This is good for development but insecure. Spring Docs

@Bean
WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
        }
    }
}
Share:
179,011

Related videos on Youtube

laprof
Author by

laprof

Updated on November 12, 2021

Comments

  • laprof
    laprof over 2 years

    I'm trying to create a user administration API for my web app. When I send an API call from my frontend to my backend, a cors error occurs. How can the cors problem be solved? I've read a lot of threads, but I haven't made any progress.

    Error after createUser() API call

    Access to XMLHttpRequest at 'http://localhost:8080/user/create' 
    from origin 'http://localhost:4200' has been blocked by CORS policy: 
    Response to preflight request doesn't pass access control check: 
    Redirect is not allowed for a preflight request.
    

    Angular header.config.ts

    export const HTTP_OPTIONS = {
      headers: new HttpHeaders({
        'Content-Type':  'application/json',
        'Access-Control-Allow-Credentials' : 'true',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE, PUT, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With',
      })
    };
    

    Angular rest-user.service.ts

      public createUser() {
        return this.httpClient.post(this.USER_ENDPOINT + 'create', HTTP_OPTIONS);
      }
    

    SpringConfig.class

    @Configuration
    @EnableWebMvc
    public class SpringConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**");
        }
    }
    

    SpringSecurityConfig.class

    @Configuration
    public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {  
        @Override  
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().authenticated()
            .and().oauth2Client()
            .and().oauth2Login();
        }
    
    }
    

    UserRestController.class

    @PostMapping("/user/create")
    @ResponseBody
    @ResponseStatus(HttpStatus.CREATED)
    public void createUser(Principal principal) throws UserAlreadyExistsException {
        userServiceFacadeImpl.createUser(principal.getName());
    }
    

    Network Message

    enter image description here

    UPDATE 20.06.19

      private createUser() {
    
        const headersObject = new HttpHeaders();
    
        this.oktaAuth.getAccessToken().then( (value) => {
    
          headersObject.append('Authorization', 'Bearer ' + value);
          headersObject.append('Content-Type', 'application/json');
    
          const httpOptions = {
            headers: headersObject
          };
    
          this.httpClient.post('http://localhost:8080/user/' + 'create', null, httpOptions);
        });
    
      }
    

    enter image description here

    • youri
      youri almost 5 years
      You need to add an origin to your CORS mapping. For example: registry.addMapping("/**").allowedOrigins("http://localhost:‌​4200");. See https://spring.io/guides/gs/rest-service-cors/
    • laprof
      laprof almost 5 years
      Thanks @youri, but it doesn't fix the problem.
  • laprof
    laprof almost 5 years
    Thanks. I added .allowedOrigins("http://localhost:4200"), wrote a filter like link and delete the header options from angular. It still doesn't seem to work properly, I get the following error: Access to XMLHttpRequest at 'http://localhost:8080/user/create' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • laprof
    laprof almost 5 years
    When I remove the options from the Angular, another error occurs: Access to XMLHttpRequest at 'http://localhost:8080/user/create' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. I tried to add a filter in spring like link but it doesn't work.
  • Romil Patel
    Romil Patel almost 5 years
    Have you replace SpringConfig class with above and please remove the header configuration on Angular and try again and let us know the outcome
  • laprof
    laprof almost 5 years
    I did, but I get this error: Access to XMLHttpRequest at 'http://localhost:8080/user/create' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • Romil Patel
    Romil Patel almost 5 years
    Hello @laprof Make sure you have added Token to Header. Go to network tab and check the header There should be Authorization with your Token
  • laprof
    laprof almost 5 years
    I have added the token as shown above in the post update, but it does not appear in the header.
  • Bhavin Hirpara
    Bhavin Hirpara over 4 years
    just a small change. also add 'Accept' in response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With,observe"); to accept multipart request.
  • TCH
    TCH about 3 years
    Adding http.cors() did the trick for me, thank you really much !
  • Youssef Boudaya
    Youssef Boudaya almost 3 years
    that ".httpBasic();" saved me from the 403 forbidden error i got stuck with in spring boot.thanks a lot :D
  • diego matos - keke
    diego matos - keke about 2 years
    THIS IS THE ANSWER. Im using Springboot and Angular.