[RESOLVIDO] Spring Boot Cors não funciona

Boa noite pessoal!!
Estou fazendo um API e na hora que fui integrar com Front-End Angular 11 começou a dar erro CORS:

Access to XMLHttpRequest at ‘http://localhost:8090/api/v1/produtos’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Porem consultado via Postman notei que no header está retornando de maneira diferente conforme imagem:

Onde deveria ser Origin = * veio conforme acima.

Eu implementei as seguintes classes no SpringBoot:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;

@Autowired
private UnauthorizedHandler unauthorizedHandler;

@Autowired
private AccessDeniedHandler accessDeniedHandler;


@Override
protected void configure(HttpSecurity http) throws Exception {
	AuthenticationManager authManager = authenticationManager();
	
	http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
    		.and()
    		.authorizeRequests()
            .antMatchers(HttpMethod.GET, "/api/v1/login","/api/v1/produtos").permitAll()
            .antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
            .permitAll()
            .anyRequest().authenticated()
            .and().httpBasic()
            .and().csrf().disable()
            .addFilter(new CorsConfig())
            .addFilter(new JwtAuthenticationFilter(authManager))
            .addFilter(new JwtAuthorizationFilter(authManager, userDetailsService))
            .exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler)
            .authenticationEntryPoint(unauthorizedHandler)
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

    auth.userDetailsService(userDetailsService).passwordEncoder(encoder);

}

E implementei um filtro também:

@Configuration
public class CorsConfig extends CorsFilter {

public CorsConfig() {
	super(configurationSource());
}

@Bean
public WebMvcConfigurer corsConfigurer() {
	return new WebMvcConfigurer() {
		@Override
		public void addCorsMappings(CorsRegistry registry) {
			CorsRegistration cors = registry.addMapping("/**");
			cors.allowedMethods("GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS");
			cors.allowedOrigins("*");
			cors.allowedHeaders("*");
		}
	};
}

private static UrlBasedCorsConfigurationSource configurationSource() {
	UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
	CorsConfiguration config = new CorsConfiguration();
	config.setAllowCredentials(true);
	config.addAllowedOrigin("*");
	config.addAllowedHeader("*");
	config.addAllowedMethod("OPTIONS");
	config.addAllowedMethod("HEAD");
	config.addAllowedMethod("GET");
	config.addAllowedMethod("PUT");
	config.addAllowedMethod("POST");
	config.addAllowedMethod("DELETE");
	config.addAllowedMethod("PATCH");
	config.addAllowedMethod("CONNECT");
	source.registerCorsConfiguration("/**", config);
	return source;
}

}

Controller:

@Api(value = "API de Cadastro de produtos")
@RestController
@RequestMapping("/api/v1/produtos")
public class ProdutoController {

@Autowired
private ProdutoService service;

@GetMapping(value="/user")
public UserDTO getUser(@RequestBody UserDTO user) {
	return user;
}


@ApiOperation(value = "Listar todos os produtos paginado")
@GetMapping
public ResponseEntity<Page<ProdutoDTO>> findAll(@RequestParam(value = "page", defaultValue = "0") Integer page,
		@RequestParam(value = "size", defaultValue = "10") Integer size) {
	Page<ProdutoDTO> produtos = service.findAll(PageRequest.of(page, size));
	return ResponseEntity.ok(produtos);
}

@ApiOperation(value = "Buscar produto por id")
@GetMapping(value = "/{id}")
public ResponseEntity<ProdutoDTO> buscarPorId(@PathVariable(name = "id", required = true) Long id) {
	ProdutoDTO dto = service.findById(id);
	return ResponseEntity.ok(dto);
}

@ApiOperation(value = "Salvar um produto na base")
@PostMapping
@Secured({ "ROLE_ADMIN" })
public ResponseEntity<ProdutoDTO> salvar(@Valid @RequestBody ProdutoDTO produto) {
	ProdutoDTO prod = service.insert(produto);
	URI location = getUri(prod.getId());
	return ResponseEntity.created(location).build();
}

@ApiOperation(value = "Alteração de dados do produto cadastrado")
@PutMapping(value = "/{id}")
public ResponseEntity<ProdutoDTO> update(@PathVariable(name = "id", required = true) Long id,
		@Valid @RequestBody ProdutoDTO dto) {
	dto.setId(id);
	ProdutoDTO prod = service.update(dto, id);
	return ResponseEntity.ok(prod);
}

@ApiOperation(value = "Deleta os dados do produto cadastrado")
@DeleteMapping(value = "/{id}")
public ResponseEntity<?> delete(@PathVariable("id") Long id) {
	service.delete(id);
	return ResponseEntity.ok().build();
}

/*
 * Método para gerar URI no retorno do post
 */
private URI getUri(Long id) {
	return ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
}

}

Desde já agradeço muito atenção.

Os cabeçalhos estão corretos?

Na foto do print que tu enviou o nome dos cabeçalhos estão em value.

Boa noite Jonathan,
Desde já agradeço muito sua atenção,
Cara esse é o problema, o retorno do get está trazendo dessa maneira da foto:

Vary : Origin
Vary : Access-Control-Request-Method
Vary : Access-Control-Request-Headers

Não sei o porque está vindo dessa maneira.
Deveria voltar dessa maneira:

Origin : *
Access-Control-Request-Method: *
Access-Control-Request-Headers: *

Já não sei o que fazer para o Angular possa consumir o serviço.

Obrigado.

Entendi, qual versão do spring você está utilizando?

Boa noite Jonathan,
Estou usando as seguintes configurações:

<?xml version="1.0" encoding="UTF-8"?>
 project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven- 
4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.4.3</version>
	<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.br.liarts</groupId>
<artifactId>apiLiArts</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>apiLiArts</name>
<description>BackEnd do trabalho da Lilian</description>
<properties>
	<java.version>8</java.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-hateoas</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-actuator</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-validation</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<scope>runtime</scope>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<!-- Swagger -->
	<dependency>
		<groupId>io.springfox</groupId>
		<artifactId>springfox-swagger2</artifactId>
		<version>2.9.2</version>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>io.springfox</groupId>
		<artifactId>springfox-swagger-ui</artifactId>
		<version>2.9.2</version>
		<scope>compile</scope>
	</dependency>
	<!-- Firebase -->
	<dependency>
		<groupId>com.google.firebase</groupId>
		<artifactId>firebase-admin</artifactId>
		<version>6.8.0</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<scope>runtime</scope>
	</dependency>
	<!-- Security -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>

	<!-- JWT -->
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt-api</artifactId>
		<version>0.10.5</version>
	</dependency>
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt-impl</artifactId>
		<version>0.10.5</version>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt-jackson</artifactId>
		<version>0.10.5</version>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-lang3</artifactId>
		<version>3.9</version>
	</dependency>

</dependencies>

<repositories>
	<repository>
		<id>spring-repo</id>
		<url>https://maven.google.com</url>
	</repository>
</repositories>

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<configuration>
				<excludes>
					<exclude>
						<groupId>org.projectlombok</groupId>
						<artifactId>lombok</artifactId>
					</exclude>
				</excludes>
			</configuration>
		</plugin>
	</plugins>
</build>

Valew!!!

Legal, cara tenta usar a seguinte implementação da classe que trata CORS:

@Configuration
public class CorsConfig {

	@Bean
	public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean() {
		CorsConfiguration config = new CorsConfiguration();
		config.setAllowCredentials(true);
		config.setAllowedOrigins(Collections.singletonList("*"));
		config.setAllowedMethods(Collections.singletonList("*"));
		config.setAllowedHeaders(Collections.singletonList("*"));

		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", config);

		FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>();
		bean.setFilter(new CorsFilter(source));
		bean.setOrder(Ordered.HIGHEST_PRECEDENCE);

		return bean;
	}

}

E na sua classe SecurityConfig, tenta utilizar este formato no método configure:

@Override
protected void configure(HttpSecurity http) throws Exception {
    AuthenticationManager authManager = authenticationManager();

    http
        .authorizeRequests()
            .antMatchers(HttpMethod.GET, "/api/v1/login","/api/v1/produtos")
                .permitAll()
            .antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
                .permitAll()
            .anyRequest()
                .authenticated()
            .and()
                .csrf().disable()
                .cors()
            .and()
                .addFilter(new JwtAuthenticationFilter(authManager))
                .addFilter(new JwtAuthorizationFilter(authManager, userDetailsService))
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
                .authenticationEntryPoint(unauthorizedHandler)
            .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

Veja se funcina na sua aplicação, eu geralmente não tenho tantas definições no método configure, mas acredito que elas não devam estar influenciando no problema.

Aliás olhando aqui, vi que tu não define uma precedência prioritária da sua classe que implementa CORS, tu pode testar sua própria implementação adicionando a seguinte anotação na sua classe:

@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
public class CorsConfig {
    ....
}

Boa noite Jonathan,
Olha sua orientação funcionou está com CORS declarado conforme imagem em azul
image

Porem ainda está vindo os “Vary” no header.

Mais resolveu o meu problema… .muito obrigado.!!!

Teria que debugar pra entender em que ponto exatamente estes cabeçalhos Vary estão sendo injetados de fato.

1 curtida