Source:  Twitter logo

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

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);
    });

  }

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

10 users liked answer #0dislike answer #010
Romil Patel profile pic
Romil Patel

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();
}
7 users liked answer #1dislike answer #17
Panagiss profile pic
Panagiss

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.

4 users liked answer #2dislike answer #24
TruckDriver profile pic
TruckDriver

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');
3 users liked answer #3dislike answer #33
Roberc profile pic
Roberc

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("*")
        }
    }
}
2 users liked answer #4dislike answer #42
Pinaki profile pic
Pinaki

You have to set the http header at the http response of your resource. So it needs to be set serverside, you can remove the "HTTP_OPTIONS"-header from your angular HTTP-Post request.

0 users liked answer #5dislike answer #50
WorksLikeACharm profile pic
WorksLikeACharm

If you are using Angular CLI on the frontend then

  • Create a proxy.conf.json in your root directory and add the following
{

   "/api": {
     "target": "your backend url",
     "secure": true,
     "changeOrigin": true,
     "pathRewrite": {
       "^/api": ""
     }
   }
 }
  • Run your development server with this command ng serve --proxy-config proxy.conf.json
  • You will access your backend in your code with the base url ${your frontend url}/api/
0 users liked answer #6dislike answer #60
Kevin Koech profile pic
Kevin Koech

I've manage to fix with the bellow in my php file:

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    // The request is using the POST method
    header("HTTP/1.1 200 OK");
    return;

}

And there's no more complains!

0 users liked answer #7dislike answer #70
Juliano Vargas profile pic
Juliano Vargas

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).