[심화] 12. Spring Security 세션 인증

백하림's avatar
Jul 25, 2025
[심화] 12. Spring Security 세션 인증

SecurityConfig

package com.metacoding.securityapp1.core; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; // 예: localhost:8080/user/asdasd, localhost:8080/user/join-form @Configuration // 이 클래스는 스프링 설정 클래스임을 명시 (빈 등록 용도) public class SecurityConfig { // 비밀번호 암호화를 위한 단방향 해시 함수 (회원가입/로그인 시 사용) @Bean public BCryptPasswordEncoder encodePwd() { return new BCryptPasswordEncoder(); } @Bean // SecurityFilterChain을 빈으로 등록하여 보안 설정을 적용함 public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // H2 Console은 iframe으로 동작하므로, 기본적으로 막혀있는 iframe을 sameOrigin으로 허용함 http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin())); // CSRF 보호 기능 비활성화 (개발/테스트용. 실서비스에서는 활성화 권장) http.csrf(configure -> configure.disable()); // 폼 로그인 방식 설정 http.formLogin(form -> form .loginPage("/login-form") // 로그인 페이지 URL (GET). 사용자가 인증 안 됐을 때 여기로 이동함 .loginProcessingUrl("/login") // 로그인 처리 URL (POST). 컨트롤러 없이도 Spring Security가 자동으로 인증 처리 .defaultSuccessUrl("/main") // 로그인 성공 후 이동할 기본 페이지 ); // 요청 URL별 인가 설정 http.authorizeHttpRequests( authorize -> authorize .requestMatchers("/main").authenticated() // 로그인만 되어 있으면 접근 가능 .requestMatchers("/user/**").hasRole("USER") // USER 권한 있어야 접근 가능 .requestMatchers("/admin/**").hasRole("ADMIN") // ADMIN 권한 있어야 접근 가능 .anyRequest().permitAll() // 그 외 모든 요청은 인증 없이 접근 허용 ); return http.build(); // 구성된 보안 필터 체인 객체를 반환 } }
🔥
SecurityFilterChain을 재정의했다 → Spring Security의 기본 필터 체인을 작성한 코드로 덮어쓴 것이고, → HttpSecurity를 사용해서 필터의 **행동 방식(인증, 인가, 로그인 방식 등)**을 하나하나 세밀하게 조정한 것임.

1. 스프링 시큐리티란?

Spring Security는 스프링 기반 웹 애플리케이션의 인증(Authentication)과 인가(Authorization)를 처리하는 보안 프레임워크다.

주요 기능:

  • 로그인(인증) 처리
  • 권한(인가) 체크
  • 세션 관리
  • CSRF/XSS 방어
  • 다양한 인증방식 지원: Form Login, HTTP Basic, JWT, OAuth2 등

🔐 인증 vs 인가

개념
설명
인증 (Authentication)
"너 누구야?" → 로그인 검증
인가 (Authorization)
"이 기능 써도 돼?" → 권한 체크

2. 스프링 시큐리티 필터 체인

요청이 들어오면 DispatcherServlet 이전에 Spring Security의 필터 체인이 먼저 실행됨.
(실제로는 서블릿 필터 다음에 DelegatingFilterProxy가 SecurityFilterChain을 호출함)

🔄 필터 체인 구조

[사용자 요청] ↓ [서블릿 필터] ↓ [DelegatingFilterProxy] ↓ [SecurityFilterChain] ├─ UsernamePasswordAuthenticationFilter ├─ BasicAuthenticationFilter ├─ ExceptionTranslationFilter ├─ FilterSecurityInterceptor └─ ...
필터는 순차적으로 실행되며, 인증/인가 처리, 예외 처리 등을 담당함.

3. 프록시 패턴과 Spring Security

Spring Security는 내부적으로 프록시 패턴을 활용함.
UserDetails, Authentication, SecurityContext 등을 가짜로 감싸서 동작을 위임함
예시:
SecurityContextHolder.getContext().getAuthentication()
→ 실제 로그인된 사용자 정보를 꺼내는 방식도 프록시 기반으로 동작

🔑 Form Login 흐름 요약

scss 복사편집 [POST /login 요청][UsernamePasswordAuthenticationFilter] ↓ (성공 시) [SecurityContextHolder.setAuthentication()][세션 저장]
  • 인증 실패 시 → 다시 /login-form으로 이동
  • 인증 성공 시 → Authentication 객체가 세션에 저장됨
폼 로그인을 사용하는 경우 UsernamePasswordAuthenticationFilter가 동작함
HTTP Basic 인증을 사용할 경우 BasicAuthenticationFilter가 동작 (동시에 둘 다 사용 불가)

5. 주요 필터 종류 정리

필터명
언제 작동?
무엇을 하는가?
UsernamePasswordAuthenticationFilter
로그인(form login) 요청 (/login 등)일 때 작동
사용자의 아이디/비밀번호를 추출하여 AuthenticationManager를 통해 인증 수행. 인증 성공 시 SecurityContextHolder에 저장
BasicAuthenticationFilter
요청 헤더에 Authorization: Basic ...이 있을 때 작동
Base64로 인코딩된 사용자 정보를 디코딩하고 인증 수행
ExceptionTranslationFilter
아래의 필터 중 인증/인가 예외가 발생했을 때 작동
인증 안된 사용자는 로그인 페이지로 redirect, 인가 실패는 403 오류 반환
SecurityContextHolderAwareRequestFilter
매 요청마다 작동
HttpServletRequest를 시큐리티 aware 객체로 래핑해, request.isUserInRole() 등 사용 가능하게 함
FilterSecurityInterceptor
컨트롤러에 진입하기 직전
인증된 사용자가 해당 자원에 접근할 권한이 있는지 최종 확인 (인가 핵심)
LogoutFilter
/logout 요청일 때 작동
세션 종료, SecurityContext 초기화, 로그아웃 후 redirect 수행
SessionManagementFilter
로그인 성공 시 또는 세션 체크 시점
세션 중복 로그인 제한, 세션 만료 처리 등 관리

6. FormLogin vs HTTP Basic

Form Login 구성 예시

java 복사편집 http.formLogin(form -> form .loginPage("/login-form") .loginProcessingUrl("/login") .defaultSuccessUrl("/main")
→ 내부적으로 동작:
1. /login-form: 로그인 페이지 2. /login (POST): 시큐리티가 가로채서 로그인 처리 3. 성공 시: /main 이동

HTTP Basic 구성 예시

http.httpBasic(); // 브라우저에서 ID/PW 팝업 뜸
→ 이 경우에는 BasicAuthenticationFilter가 동작하고 UsernamePasswordAuthenticationFilter는 동작 안함

7. JWT 기반 로그인 (간단 요약)

JWT를 사용할 경우 세션을 사용하지 않고, 인증 정보를 직접 SecurityContext에 저장함

강제로 인증 객체 삽입하기

User user = User.builder().id(id).role(role).build(); MyUserDetails myUserDetails = new MyUserDetails(user); Authentication authentication = new UsernamePasswordAuthenticationToken( myUserDetails, myUserDetails.getPassword(), myUserDetails.getAuthorities() ); SecurityContextHolder.getContext().setAuthentication(authentication);
→ 이 코드를 통해 세션 없이도 인증 상태를 수동으로 주입 가능함

🔗 참고 자료


SecurityContextHolder란?

한 줄 정의:

인증 객체(Authentication)를 저장하고 관리하는 ThreadLocal 기반의 보안 컨텍스트 저장소

🧠 작동 원리 요약

인증 흐름 중 이 위치에서 등장:

1. 로그인 요청 (POST /login) ↓ 2. UsernamePasswordAuthenticationFilter ↓ 3. 로그인 성공 시 Authentication 객체 생성 ↓ ✅ SecurityContextHolder.getContext().setAuthentication(authentication) ↓ 4. SecurityContext가 세션에 저장됨
  • 이렇게 저장된 Authentication은 이후 모든 요청에서 다시 꺼내서 사용
  • 꺼내는 방식:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
→ 현재 로그인한 유저의 정보를 여기서 얻을 수 있음.

🔐 내부 구조 요약

SecurityContextHolder → SecurityContext (보안 컨텍스트) → Authentication (인증 정보) → Principal (사용자 정보) → Credentials (비밀번호) → Authorities (권한 목록)

📦 왜 ThreadLocal 기반인가?

  • 사용자 요청은 스레드마다 독립적으로 처리
  • 그래서 로그인 정보(인증 정보)를 ThreadLocal로 담아두면
    • 하나의 사용자 요청이 처리되는 동안 안전하게 인증 정보를 사용할 수 있음

✅ 로그인 후 인증 객체 접근 예시

Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String username = authentication.getName(); // 로그인한 username Collection<? extends GrantedAuthority> roles = authentication.getAuthorities();

📌 한 줄 요약

이름
역할
비고
SecurityContextHolder
인증 정보를 담는 글로벌 저장소
ThreadLocal 기반
SecurityContext
실제 인증 정보(Authentication)를 담고 있음
세션과 연결됨
Authentication
로그인 사용자 정보, 권한 등을 포함함
인증 여부 판단 기준

🔄 그림으로 전체 요청 흐름 요약

[POST /login][UsernamePasswordAuthenticationFilter][Authentication 생성 성공][SecurityContextHolder.setContext(인증 정보)][세션에 SecurityContext 저장][다음 요청부터 SecurityContextHolder.getContext()로 인증 정보 꺼냄]

🌐 전체 요청 처리 흐름 요약 (Spring MVC + Spring Security)

[사용자 요청 (URL 입력)][WAS (Tomcat)][Spring Security Filter Chain] ← 인증/인가 필터들 ↓ [DispatcherServlet (DS)] ← 요청의 핵심 분기 처리 ↓ [HandlerMapping] ← 어떤 Controller 메서드가 호출될지 결정 ↓ [Controller] ← 요청 처리 시작 ↓ [Service] ← 비즈니스 로직 처리 ↓ [Repository] ← DB 연산 (JPA, MyBatis 등) ↓ [Database] ← 실제 데이터 저장/조회 ↓ [응답 생성 → JSON / View 등][DispatcherServlet][WAS → 사용자에게 HTTP 응답 전송]

BasicAuthentication

🔥
매 요청(Request)마다 클라이언트가 아이디/비밀번호를 Authorization 헤더에 실어 보내고
서버는 그걸 디코딩해서 인증하는 방식임.
Share article

harimmon