1차 프로젝트에서 SecurityConfig 잡다가 고생했다. 설정 하나 잘못 건드렸다가 모든 요청이 막혀버리거나, 다 뚫려버리거나 둘 중 하나였다. 그때 겪으면서 배운 거 정리해두려고 한다.
Spring Security란
Spring 애플리케이션의 인증/로그인이랑 인가/접근 권한을 담당하는 프레임워크다.
모든 HTTP 요청이 컨트롤러에 도달하기 전에 필터 체인을 거치는데 Spring Security가 그 과정에서 요청을 검사한다.
allowlist vs denylist
처음에 SecurityConfig를 allowlist 방식으로 짰다. 허용할 경로만 명시하고 나머지는 전부 막는 방식이다.
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
근데 Postman으로 요청을 보냈더니 API 응답 대신 Spring Security 기본 로그인 페이지가 응답으로 왔다.
처음엔 뭐가 문제인지 몰랐는데 허용 목록에 경로가 하나 빠져있던 거였다.
allowlist 방식은 허용할 경로를 하나하나 다 적어줘야 해서 실수가 생기기 쉽다.
그래서 denylist 방식으로 바꿨다. 막을 경로만 명시하고 나머지는 허용하는 방식이라 관리가 훨씬 편했다.
경로가 많아질수록 allowlist는 실수가 늘어나는데 denylist는 그 부분이 해결됐다.
CSRF 문제
CSRF는 사용자가 의도하지 않은 요청을 서버에 보내게 만드는 공격 방식이다.
로그인된 상태에서 악성 사이트에 접속했을 때 그 사이트가 몰래 내 계정으로 요청을 보내는 식이다.
Spring Security는 이걸 막으려고 기본적으로 POST 요청에 CSRF 토큰을 요구한다.
근데 JWT 방식은 세션을 아예 안 쓰기 때문에 CSRF 공격 자체가 성립이 안 된다.
그래서 비활성화하는 게 맞는데 이걸 모르고 뒀다가 POST 요청마다 403이 떴다. 분명 경로도 허용했는데 왜 막히는지 몰라서 한참 헤맸다.
.csrf(AbstractHttpConfigurer::disable)
CORS 문제
CORS는 브라우저가 다른 출처의 서버에 요청할 때 적용되는 보안 정책이다.
도메인, 포트, 프로토콜 중 하나라도 다르면 다른 출처로 본다.
프론트가 localhost:5173이고 백엔드가 localhost:8080이면 포트가 달라서 막힌다.
백엔드에서 허용할 출처를 명시해줘야 통신이 되는데 Vite 개발 서버가 실행할 때마다 포트가 5173 → 5174 → 5175로 바뀌는 걸 몰랐다. 허용 목록에 없는 포트라 계속 막혔고, 에러는 프론트에서 났는데 원인은 백엔드 설정에 있었다.
config.setAllowedOrigins(List.of("http://localhost:5173", "http://localhost:5174", "http://localhost:5175"));
느낀 점
에러가 프론트에서 나도 원인은 백엔드 Security 설정인 경우가 많았다.
처음엔 프론트 코드만 계속 고쳤는데 아무 변화가 없어서 답답했다.
그때부터 에러 나면 Security 설정부터 의심하는게 조금 생긴거 같다. CSRF, CORS, 경로 누락 이 세 가지가 제일 자주 터졌다.