6

I have cors enabled for all origins and headers but I still get an cors error when I call a get method from my angular app to spring boot.

Cors error from console:

Access to XMLHttpRequest at 'http://localhost:8080/api/users/test@ronny.nl' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

My controller (I call the getbyemail):

@CrossOrigin(origins = "*", allowedHeaders = "*")
@RestController
@RequestMapping(value = "/api/users", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(final UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public List<UserDTO> getAllUsers() {
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public UserDTO getUser(@PathVariable final Long id) {
        return userService.get(id);
    }

    @CrossOrigin(origins = "*", allowedHeaders = "*")
    @GetMapping("/{email}")
    public UserDTO getUserByMail(@PathVariable String email) {
        return userService.getByEmail(email);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Long createUser(@RequestBody @Valid final UserDTO userDTO) {
        return userService.create(userDTO);
    }

    @PutMapping("/{id}")
    public void updateUser(@PathVariable final Long id, @RequestBody @Valid final UserDTO userDTO) {
        userService.update(id, userDTO);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable final Long id) {
        userService.delete(id);
    }

}

Where I call the get from my angular app:

onSubmit(): void {
    this.submitted = true;
    this.wrongInput = false;
    this.loginService.getLogin<User>(this.loginForm.value.email).subscribe((response) => {
      this.tempUser = response;
      console.log(this.tempUser);
      if (this.loginForm.value.email === this.tempUser.email && this.loginForm.value.password === this.tempUser.password) {
        this.localStorageService.save(this.tempUser);
        this.router.navigate(['/home']);
        console.log(true);
      }
      else {
        this.wrongInput = true;
      }
    });
  }

I also tried to add an DevCorsConfiguration:

package com.team13.triviaquiz.triviaquizserver.config;

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

@Configuration
@Profile("development")
public class DevCorsConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**").allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS");
    }
}

And added the profile in my application.properties:

application.properties
spring.profiles.active=development
#enabling h2 console
spring.h2.console.enabled=true
#fixed URL for H2 (necessary from Spring 2.3.0)
spring.datasource.url=jdbc:h2:mem:triviaquizserver
#turn statistics on
spring.jpa.properties.hibernate.generate_statistics = true
logging.level.org.hibernate.stat=debug
#show all queries
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.type=trace

But still no luck...

Ronny Giezen
  • 351
  • 3
  • 11

5 Answers5

7

This could be due to a change in Spring libraries, thus affecting Spring Boot 2.4.0. Look here https://github.com/spring-projects/spring-framework/issues/26111 and to its related ticket https://github.com/spring-projects/spring-framework/pull/26108

EDIT Here the original message from the 1st link:

#25016 introduced the ability to configure allowedOriginPatterns in addition to just allowedOrigins. It lets you define more flexible patterns while the latter is literally the value to return in the Access-Control-Allow-Origin header and for that "*" is not allowed in combination with allowCredentials=true. The change introduced equivalent allowedOriginPatterns methods in the WebMvc and the WebFlux config, but not in the SockJS config and the AbstractSocketJsService.

I'll add those for 5.3.2. You'll then need to switch to allowedOriginPatterns instead of allowedOrigins but that gives you an option to define more precisely the allowed domain patterns. In the mean time, you might be able to work around by listing specific domains if that's feasible.

mau
  • 86
  • 3
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/27849593) – Ivan Aracki Dec 15 '20 at 12:10
  • Makes sense, edited the answer. Thanks for the hint – mau Dec 15 '20 at 15:12
3

The reason behind this issue has been clearly mentioned in the answer, briefly CorsRegistry#allowCredentials(true) cannot be used with the default value of CorsRegistry#allowedOrigins() (By default all origins are allowed if you haven't set this property unless any of CorsRegistry#allowedOriginPatterns() is set)

  • Elucidating the issue with the most typical Spring boot corsMappings:
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedMethods("*").allowCredentials(true);
            }
        };
    }

In the above example allowCredentials is set to true, at the same time allowedOrigins is not configured which means by default all origins are allowed (The allowedOrigins value will be *).

  • To resolve this issue you have to explicitly set the allowedOrigins or allowedOriginPatterns. In the following code snippet, the allowedOriginPatterns is used and the value is set to -> "https://*.domain.com". This wildcard pattern matches any host from domain.com and url patterns like https:microservice.division.domain.com
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedMethods("*").allowedOriginPatterns("https://*.domain.com");
            }
        };
    }

Complete code snippet link

Prasanth Rajendran
  • 1,596
  • 1
  • 20
  • 26
0

try the following on your Controller

@CrossOrigin(origins = "*", allowCredentials = "true", allowedHeaders = "*", exposedHeaders = "If-Match")
dpk
  • 593
  • 4
  • 14
  • then I get another error: When allowCredentials is true, allowedOrigins cannot contain the special value "*"since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead. – Ronny Giezen Nov 18 '20 at 12:05
0

The cors error came from Handling ambiguous handler errors for my two get methods. I got two get methods in my controller one took an integer the other a String. So it couldn't really figure out to parse a string or int (I think) I've changed bot paths of the get methods to:

 @GetMapping("/id/{id}")
    public UserDTO getUser(@PathVariable final Long id) {
        return userService.get(id);
    }

    @GetMapping("/email/{email}")
    public UserDTO getUserByMail(@PathVariable String email) {
        return userService.getByEmail(email);
    }

This fixed the cors error aswell with @CrossOrigin(origins = "*", allowedHeaders = "*") in the controller.

Ronny Giezen
  • 351
  • 3
  • 11
0

My Scenario when I got the error is:

I've added the Spring Boot Security Dependency in my Spring Boot Server Application.

Similar to your trail, I've added the CrossOrigin() Annotation to resolve the issue. But the issue didn't get solved.

I solved the issue by added the basic authentication to my header options of the request.

Please refer to the post to know about adding basic authentication to the header:

Angular 6 HTTP Get request with HTTP-Basic authentication

import { HttpHeaders } from '@angular/common/http';

const httpOptions = {
headers: new HttpHeaders({
'Content-Type':  'application/json',
'Authorization': 'Basic ' + btoa('username:password')
})
};

Replace username and password with your username and password. By default username in Spring boot is 'user' and password will be generated on the console where the spring boot application is started.

NOTE: You also have to add the CrossOrigin Annotation to your RequestMapping as well.

Since You are using Basic Authentication, You should add WebSecurityConfiguration to allow Security Configuration as below:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
        http.cors();
    }
}

References:

Hope the solution have helped you.

Thanks Chandu

Chandu
  • 184
  • 2
  • 11