요청을 보낸 주체가 누구인지 확인합니다. 실패하면 401입니다. 아이디·비밀번호 확인, 토큰 검증이 여기 속합니다.
이 파트에서 다루는 내용
Spring Security는 컨트롤러 앞단의 필터입니다
Security를 의존성에 추가하는 순간 모든 요청이 필터 체인을 먼저 지나갑니다. 컨트롤러 코드를 한 줄도 바꾸지 않았는데 로그인 화면이 뜨는 이유가 이것입니다.
그래서 인증·인가 문제는 컨트롤러에서 디버깅해도 원인이 보이지 않습니다. 필터 단계에서 이미 차단된 요청은 컨트롤러에 도달조차 하지 않습니다.
확인된 주체가 해당 자원에 접근할 권한이 있는지 판단합니다. 실패하면 403입니다.
인증·인가 실패는 @RestControllerAdvice로 잡히지 않습니다. 응답 형식을 통일하려면 별도 진입점 설정이 필요합니다.
설정은 SecurityFilterChain 빈으로 합니다
예전에는 설정 클래스를 상속해 메서드를 재정의하는 방식이었지만, 지금은 SecurityFilterChain 빈을 등록하는 방식만 사용합니다.
경로별 접근 규칙은 위에서부터 순서대로 평가됩니다. 넓은 규칙을 위에 두면 아래의 구체적인 규칙이 무시되므로 순서가 중요합니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.csrf(csrf -> csrf.disable()); // 아래 주의 사항을 반드시 확인하세요
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}CSRF 비활성화는 토큰 기반 무상태 API에서만 타당합니다. 브라우저 세션 쿠키를 쓰는 서비스에서 끄면 위조 요청에 그대로 노출됩니다. 예제를 복사할 때 가장 자주 잘못 따라 하는 부분입니다.
세션과 토큰은 장단점이 분명합니다
- 로그아웃과 강제 만료가 즉시 반영됩니다
- 서버가 여러 대면 세션 저장소를 공유해야 합니다
- 브라우저 기반 서비스에 적합합니다
- CSRF 보호가 필요합니다
- 서버가 상태를 갖지 않아 확장이 쉽습니다
- 발급한 토큰을 즉시 무효화하기 어렵습니다
- 모바일 앱이나 서버 간 통신에 적합합니다
- 만료 시간을 짧게 두고 갱신 토큰을 함께 씁니다
- 본문은 암호화가 아니라 인코딩입니다. 누구나 내용을 볼 수 있습니다
- 개인정보나 권한 상세를 담으면 그대로 노출됩니다
- 탈취되면 만료까지 유효하므로 만료 시간이 짧아야 합니다
로컬 스토리지에 두면 XSS로 탈취될 수 있고, 쿠키에 두면 CSRF 대비가 필요합니다. 어느 쪽이든 대응책을 함께 마련해야 합니다.
실무에서 반복되는 보안 사고
MD5나 SHA-1은 비밀번호 저장 용도로 부적합합니다. BCrypt처럼 느리게 설계된 해시를 씁니다.
경로 규칙으로 관리자 화면만 막고, 데이터 소유자 검사를 하지 않으면 다른 사람의 자원 식별자를 넣어 접근할 수 있습니다.
환경 변수와 빈 목록이 외부에 공개될 수 있습니다. 노출 엔드포인트를 제한하고 접근을 통제합니다. Part 13에서 다룹니다.
'없는 아이디'와 '비밀번호 불일치'를 구분해 알려 주면 공격자가 유효한 계정 목록을 만들 수 있습니다. 문구를 통일합니다.
CORS는 브라우저의 교차 출처 요청 허용 규칙이고 CSRF는 위조 요청 방어입니다. CORS를 열기 위해 CSRF를 끄는 것은 잘못된 대응입니다.
인증 실패 응답에 스택트레이스나 SQL이 담기면 공격에 활용됩니다. 응답 문구는 고정합니다.
보안은 프레임워크 설정만으로 완성되지 않습니다. 필터에서 인증을 처리했더라도, 서비스 계층에서 '이 데이터가 이 사용자의 것인가'를 확인하지 않으면 뚫립니다. 인증은 입구, 인가는 데이터 단위까지입니다.
3.x와 4.x 차이
본문은 현장에서 가장 많이 쓰는 3.x 기준입니다. 4.x에서 달라진 부분만 아래에 정리합니다.
- Spring Boot 3.x는 Spring Security 6을 사용합니다. WebSecurityConfigurerAdapter는 Security 6에서 제거됐으므로, 2.x 시절 예제를 그대로 복사하면 컴파일되지 않습니다.
- Spring Boot 4.x는 Spring Security 7을 사용합니다. 람다 DSL만 지원하며 예전의 and() 체이닝 방식은 제거됐습니다.
- 인터넷 예제 상당수가 아직 구버전 문법입니다. 검색한 코드가 동작하지 않으면 문법이 아니라 버전 문제인지 먼저 확인합니다.