Added corsConfigurationSource and still an error "has been blocked by CORS policy"

Viewed 496

I'm trying to connect Spring Security to my project. Created the Security Config class

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .cors().and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/auth/api/v1/user/register").permitAll()
                .antMatchers("/auth/api/v1/admin/*").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .apply(new JwtConfigurer(jwtTokenProvider));
    }
}

I am sending a request from the browser for registration

http://localhost:15001/auth/api/v1/user/register

and I get an answer:

Access to XMLHttpRequest at 'http://localhost:15001/auth/api/v1/user/register' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

According to the Spring documentation, I add the corsConfigurationSource method:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .cors().and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/auth/api/v1/user/register").permitAll()
                .antMatchers("/auth/api/v1/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .apply(new JwtConfigurer(jwtTokenProvider));
    }
}

I am sending a request from the browser for registration

http://localhost:15001/auth/api/v1/user/register

and I still get the same error

Access to XMLHttpRequest at 'http://localhost:15001/auth/api/v1/user/register' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Why didn't the error disappear?

I know there is another way to add on the controller

@CrossOrigin(origins="http://localhost:4200″)

You can also add to the header. but I want to figure out why this method doesn't work.

pom.xml

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.4.12</version>
    <relativePath/>
</parent>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>${jwt.version}</version>
  </dependency>
  <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>${postgres.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
  </dependency>
</dependencies>

Here is the code where I form the answer

final UserDto userDto = userToUserDtoConverter.convert(optionalUser.get());
if (password.equals(UserUtils.encryptText(optionalAuth.get().getPassword()))) {
  final HttpHeaders responseHeaders = new HttpHeaders();
  final String token = jwtTokenProvider.createToken(optionalAuth.get().getName());
  return confirmUserRegister(userDto, "Пользователь авторизован",
    HttpStatus.OK, responseHeaders);
}

protected ResponseEntity<DataResponse<UserDto>> confirmUserRegister(
    UserDto userDto, String message, HttpStatus httpStatus, HttpHeaders responseHeaders) {
  final DataResponse<UserDto> response = new DataResponse<>();
  response.setStatus(StatusType.SUCCESSFUL);
  response.setData(userDto);
  response.addMessage(httpStatus.value(), message);
  return new ResponseEntity<>(response, responseHeaders, httpStatus);
}

Here is the link to the project enter link description here

The project is still very raw. And this is a copy of the project, so you can edit as you like

3 Answers

If this is a local environment, you don't need to configure Spring, instead you modify angular configuration.

Create a file proxy.conf.json in your project's src/ folder.

Add the following content to the new proxy file:

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

In the CLI configuration file, angular.json, add the proxyConfig option to the serve target:

...

"architect": {
  "serve": {
    "builder": "@angular-devkit/build-angular:dev-server",
    "options": {
      "browserTarget": "your-application-name:build",
      "proxyConfig": "src/proxy.conf.json"
    },
...

To run the development server with this proxy configuration, call ng serve.

More details are here. The trouble with configuring Spring CORS is that:

  • you are trying to solve a development environment specific problem
  • this may leak CORS configuration into production setup where they aren't required unless you do actually want CORS set up.

Now, what to do in production ?

It actually depends on how you bundle your code.

If your UI + Java code is going to be in same deployable, WAR or JAR, you don't need to configure CORS because they will be deployed on same domain (https://apps.example.com) or on same context root (https://apps.example.com/app).

You do need to configure CORS when your UI and java code is not on same domain or you want apps deployed on other domains (https://apps.example.**com**) to access your APIs from their page.

Please note in Spring, when you set

configuration.setAllowCredentials(true);

Spring does not accepts:

configuration.setAllowedOrigins(Arrays.asList("*"));

You are required to configure required origins one by one like this:

configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));

You configuration should be like this.

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*"));
    configuration.setAllowCredentials(true);
    configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers", "Access-Control-Allow-Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", "Origin", "Cache-Control", "Content-Type", "Authorization"));
    configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

you have to update your configure method with this :

Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .**cors().disable().and().csrf().disable()**
                .
                .
                .
    }
Related