Hello Stack Overflow community,
I am encountering a CORS (Cross-Origin Resource Sharing) error in my Spring Boot application, and I need some guidance on resolving it. The issue is that the error only occurs for specific controllers within my application. Here's the scenario:
I have a Spring Boot application with multiple controllers, and I have configured CORS globally in my application using @CrossOrigin annotations in my controllers. However, one of my controllers is still generating CORS errors, while the other work fine.
this is the controller not working:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.CrossOrigin;
import com.sinandemir.todoapp.dto.requests.TodoRequest;
import com.sinandemir.todoapp.dto.responses.TodoResponse;
import com.sinandemir.todoapp.services.TodoService;
@RestController
@RequestMapping("api/v1/todos")
@CrossOrigin("*")
public class TodoController {
private TodoService todoService;
public TodoController(TodoService todoService) {
this.todoService = todoService;
}
@PreAuthorize("hasRole('ADMIN')")
@PostMapping
public ResponseEntity<TodoResponse> addTodo(@RequestBody TodoRequest todoRequest) {
TodoResponse savedTodo = todoService.addTodo(todoRequest);
return new ResponseEntity<TodoResponse>(savedTodo, HttpStatus.CREATED);
}
@PreAuthorize("hasAnyRole('ADMIN','USER')")
@GetMapping("{todoId}")
public ResponseEntity<TodoResponse> getTodoById(@PathVariable Long todoId) {
TodoResponse todo = todoService.getTodo(todoId);
return new ResponseEntity<TodoResponse>(todo, HttpStatus.OK);
}
@PreAuthorize("hasAnyRole('ADMIN','USER')")
@GetMapping
public ResponseEntity<Page<TodoResponse>> getAllTodosWithPagination(Pageable pageable) {
Page<TodoResponse> todos = todoService.getAllTodosWithPagination(pageable);
return new ResponseEntity<Page<TodoResponse>>(todos, HttpStatus.OK);
}
@PreAuthorize("hasRole('ADMIN')")
@PutMapping("{todoId}")
public ResponseEntity<TodoResponse> updateTodo(@RequestBody TodoRequest todoRequest, @PathVariable Long todoId) {
TodoResponse todo = todoService.updateTodo(todoRequest, todoId);
return new ResponseEntity<TodoResponse>(todo, HttpStatus.OK);
}
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("{todoId}")
public ResponseEntity<String> deleteTodo(@PathVariable Long todoId) {
todoService.deleteTodo(todoId);
return new ResponseEntity<String>("todo deleted successfully.", HttpStatus.OK);
}
@PreAuthorize("hasAnyRole('ADMIN','USER')")
@PatchMapping("{todoId}")
public ResponseEntity<TodoResponse> changeCompletedStatus(@PathVariable Long todoId) {
TodoResponse todo = todoService.changeCompletedStatus(todoId);
return new ResponseEntity<TodoResponse>(todo, HttpStatus.OK);
}
}
this is the controller working:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.sinandemir.todoapp.dto.requests.RefreshTokenRequest;
import com.sinandemir.todoapp.dto.requests.UserLoginRequest;
import com.sinandemir.todoapp.dto.requests.UserRegisterRequest;
import com.sinandemir.todoapp.dto.responses.UserLoginResponse;
import com.sinandemir.todoapp.dto.responses.UserRegisterResponse;
import com.sinandemir.todoapp.services.AuthService;
import com.sinandemir.todoapp.services.RefreshTokenService;
import io.jsonwebtoken.ExpiredJwtException;
@RestController
@RequestMapping("api/v1/auth")
@CrossOrigin("*")
public class AuthController {
private AuthService authService;
private RefreshTokenService refreshTokenService;
public AuthController(AuthService authService, RefreshTokenService refreshTokenService) {
this.authService = authService;
this.refreshTokenService = refreshTokenService;
}
@PostMapping("register")
public ResponseEntity<UserRegisterResponse> register(@RequestBody UserRegisterRequest registerRequest) {
UserRegisterResponse user = authService.register(registerRequest);
return new ResponseEntity<UserRegisterResponse>(user, HttpStatus.CREATED);
}
@PostMapping("login")
public ResponseEntity<UserLoginResponse> login(@RequestBody UserLoginRequest loginRequest) {
UserLoginResponse user = authService.login(loginRequest);
return new ResponseEntity<UserLoginResponse>(user, HttpStatus.OK);
}
@PostMapping("refresh-token")
public ResponseEntity<String> refreshToken(@RequestBody RefreshTokenRequest refreshTokenRequest) {
try {
String newJwtToken = authService.refreshAccessToken(refreshTokenRequest.getRefreshToken());
if (newJwtToken != null) {
return new ResponseEntity<String>(newJwtToken, HttpStatus.CREATED);
} else {
return new ResponseEntity<String>("Token refresh failed.", HttpStatus.BAD_REQUEST);
}
} catch (ExpiredJwtException e) {
refreshTokenService.deleteRefreshTokenByRefreshToken(refreshTokenRequest.getRefreshToken());
return new ResponseEntity<String>("Session has expired.", HttpStatus.UNAUTHORIZED);
}
}
}
this is my Spring security config class:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.sinandemir.todoapp.security.JwtAuthenticationEntryPoint;
import com.sinandemir.todoapp.security.JwtAuthenticationFilter;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
private UserDetailsService userDetailsService;
private JwtAuthenticationEntryPoint authenticationEntryPoint;
private JwtAuthenticationFilter authenticationFilter;
public SecurityConfig(UserDetailsService userDetailsService, JwtAuthenticationEntryPoint authenticationEntryPoint, JwtAuthenticationFilter authenticationFilter){
this.userDetailsService = userDetailsService;
this.authenticationEntryPoint = authenticationEntryPoint;
this.authenticationFilter = authenticationFilter;
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrf) -> csrf.disable())
.authorizeHttpRequests((authorize) -> {
authorize.requestMatchers("api/v1/auth/**").permitAll();
authorize.anyRequest().authenticated();
}).httpBasic(Customizer.withDefaults());
http.exceptionHandling(exception -> exception.authenticationEntryPoint(authenticationEntryPoint));
http.addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager manager(AuthenticationConfiguration configuration) throws Exception{
return configuration.getAuthenticationManager();
}
}
another way i tried for cors error:
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.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:5173")
.allowedMethods("*")
.allowedHeaders("*");
}
}
this is request from my frontend i got cors error:
export const getTodosWithPagination = async (
token: string,
page: number,
size: number
) => {
try {
const data = await axios.get(BASE_URL + `todos?page=${page}&size=${size}`, {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
},
});
const response = data.data;
return response;
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response?.status === 401) {
throw new Error("Geçersiz token.");
}
if (error.response?.status !== 201) {
throw new Error("Bilinmeyen bir hata!");
}
} else return error;
}
};
my useEffect in ReactJs:
useEffect(() => {
const token = cookies.auth?.accessToken
const getTodos = async () => {
if (token) {
const response = await getTodosWithPagination(token, 0, 10)
console.log(response);
}
return null
}
getTodos()
}, [])
thank you all!!!!!!!
SOLVED! you can solve as in comment or you can edit securityFilterChaine like this:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrf) -> csrf.disable())
.authorizeHttpRequests((authorize) -> {
authorize.requestMatchers("api/v1/auth/**").permitAll();
authorize.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll(); // added new line
authorize.anyRequest().authenticated();
}).httpBasic(Customizer.withDefaults());
http.exceptionHandling(exception -> exception.authenticationEntryPoint(authenticationEntryPoint));
http.addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
With Spring security, CORS makes a preflight request that does not contain any cookies. You need to enable CORS support through Spring security.
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors(cors -> cors.configurationSource(corsConfigurationSource()))...
}
}
And configure CorsConfigurationSource bean like:
@Bean
CorsConfigurationSource corsConfigurationSource()
{
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173"));
configuration.setAllowedMethods(Arrays.asList("GET","POST", "PUT", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}