
이 글은 2025년 06월 09일 작성된 글입니다.
Spring Security를 프로젝트에 적용하면서 CustomAuthenticationFilter, SecurityContext, JWT 인증, 인가 처리, CORS 설정까지 학습했다. 기존에 직접 구현하던 인증/인가 로직을 Spring Security가 이해할 수 있도록 연결하는 과정에 집중했다.
Spring Security가 JWT와 apiKey를 이해하지 못하기 때문에 필터를 추가하여 먼저 인증을 수행하도록 구현했다.
if (accessToken != null || apiKey != null) {
// 인증 처리
}
사용자 요청
↓
CustomAuthenticationFilter
↓
UsernamePasswordAuthenticationFilter
↓
인가 처리
↓
Controller
필터는 컨트롤러보다 먼저 실행되기 때문에 ControllerAdvice가 처리할 수 없다.
try {
// 인증 처리
} catch (Exception e) {
response.setStatus(401);
}
Spring Security는 현재 요청 사용자의 정보를 SecurityContext에 저장한다.
SecurityContextHolder
.getContext()
.setAuthentication(authentication);
요청
↓
인증
↓
SecurityContext 저장
↓
인가
↓
Controller 실행
↓
SecurityContext 제거
Spring Security 기본 User 클래스에는 프로젝트에서 필요한 id, nickname 정보가 없다.
public class SecurityUser implements UserDetails {
private Long id;
private String nickname;
}
기존에 컨트롤러에서 직접 권한을 검사하던 방식을 제거하고 Security 설정으로 이동했다.
.requestMatchers("/api/*/adm/**")
.hasRole("ADMIN")
* → 한 단계
** → 여러 단계
/api/*/adm/**
예시
/api/v1/adm/members
/api/v2/adm/posts/1
/api/test/adm/stats/today
CustomAuthenticationFilter가 인증을 끝냈기 때문에 Rq에서 다시 인증할 필요가 없어졌다.
Rq.getActor()
-> 직접 인증
SecurityContext
-> Principal
-> Member 복원
REST API에서 사용하지 않는 Security 기능을 비활성화했다.
http
.formLogin(form -> form.disable())
.httpBasic(httpBasic -> httpBasic.disable());
apiKey 쿠키에 보안 옵션을 추가했다.
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setMaxAge(31536000);
| 옵션 | 설명 |
|---|---|
| None | 모든 요청 허용 |
| Lax | GET 중심 허용 |
| Strict | 동일 사이트만 허용 |
프론트엔드와 백엔드가 서로 다른 도메인에서 통신할 수 있도록 설정했다.
configuration.addAllowedOrigin("http://localhost:3000");
테스트 코드에서 인증 관련 반복 코드를 제거했다.
.header("Authorization", "Bearer ...")
@WithUserDetails("user1")
개발 환경에서는 DEBUG 로그를 사용하도록 설정했다.
| 레벨 | 설명 |
|---|---|
| TRACE | 가장 상세 |
| DEBUG | 개발용 |
| INFO | 기본 |
| WARN | 경고 |
| ERROR | 오류 |
logging:
level:
com.back: DEBUG
SecurityConfig, Filter, MemberService, Rq 사이에서 순환참조가 발생했다.
CustomAuthenticationFilter
↓
Rq
↓
MemberService
↓
SecurityConfig
↓
CustomAuthenticationFilter