Security FilterChain과 요청 흐름

3줄 요약

  • Spring Security는 Controller 내부 기능이 아니라 Servlet FilterChain 위에서 동작한다.
  • 요청은 Servlet Container, FilterChain, Spring Security FilterChain을 통과한 뒤 DispatcherServlet과 Controller에 도착한다.
  • Controller breakpoint가 걸리지 않는 401, 403, 302, CORS, CSRF 문제는 먼저 Security FilterChain 통과 여부를 확인해야 한다.

핵심 정리

  • Servlet Container는 HTTP 요청을 받고 HttpServletRequest, HttpServletResponse를 만든 뒤 FilterChain을 실행한다.
  • Spring Security는 DelegatingFilterProxy, FilterChainProxy, SecurityFilterChain 구조로 Servlet Filter 영역에 연결된다.
  • FilterChainProxy는 현재 요청에 맞는 SecurityFilterChain을 선택하고, 그 안의 Security Filter들을 정해진 순서로 실행한다.
  • 여러 SecurityFilterChain이 있어도 현재 요청에 대해 첫 번째로 매칭된 체인만 실행된다. 그래서 체인 matcher 순서는 좁은 범위에서 넓은 범위로 두는 것이 안전하다.
  • 인증 필터는 요청에서 자격 증명을 읽어 Authentication을 만들고, 인가 필터는 현재 인증 정보와 요청 규칙을 비교한다.
  • chain.doFilter()가 호출되지 않거나 Security 필터에서 응답이 결정되면 Controller까지 도달하지 않는다.

헷갈리는 지점

  • Security를 Controller 앞의 인터셉터 정도로 생각하기 쉽다. MVC 앱에서 Controller와 함께 설정하기 때문이다.
    • 핵심은 Spring Security의 Servlet 지원이 Filter 기반이라는 점이다.
    • Controller에 도착하기 전 인증, 인가, CSRF, CORS, 예외 변환이 끝날 수 있다.
  • 401/403이 나오면 Controller 코드를 먼저 의심하기 쉽다. API 응답이 Controller에서 만들어질 것처럼 느껴지기 때문이다.
    • 핵심은 Controller breakpoint가 안 걸리면 이미 앞단 필터에서 멈췄을 가능성이 크다는 점이다.
    • Security debug 로그와 필터 체인을 먼저 확인한다.
  • 필터 순서를 몰라도 된다고만 생각하기 쉽다. 대부분 자동 설정으로 동작하기 때문이다.
    • 핵심은 커스텀 필터를 추가하거나 문제를 추적할 때 순서가 매우 중요하다는 점이다.
    • 인증은 인가보다 앞서야 하고, 예외 변환 필터의 위치도 응답 처리에 영향을 준다.

확인 질문

  • Spring Security는 Spring MVC Controller 내부에서 동작하는가?
    • 아니다. Servlet FilterChain 위에서 Controller 이전에 동작한다.
  • Controller breakpoint가 걸리지 않는 401/403에서 먼저 볼 곳은 어디인가?
    • Spring Security FilterChain과 각 보안 필터의 처리 결과를 먼저 봐야 한다.
  • SecurityFilterChain은 무엇을 결정하는가?
    • 현재 요청에 어떤 Spring Security Filter들이 적용될지 결정한다.

Authentication 구조

3줄 요약

  • Authentication은 인증 요청이자 인증 완료된 현재 사용자 정보를 표현하는 핵심 인터페이스다.
  • AuthenticationManager는 인증 처리를 정의하고, 대표 구현인 ProviderManager는 여러 AuthenticationProvider에게 검증을 위임한다.
  • 인증 성공 결과는 SecurityContext에 담겨 SecurityContextHolder를 통해 현재 요청에서 참조된다.

핵심 정리

  • 인증 전 Authentication은 사용자가 제출한 자격 증명을 담은 요청 객체다.
  • 인증 후 Authentication은 principal, authorities, 인증 상태를 가진 현재 사용자 정보다.
  • AuthenticationManager는 인증 요청을 받아 성공한 Authentication을 반환하거나 실패 예외를 던진다.
  • ProviderManager는 여러 AuthenticationProvider 중 처리 가능한 Provider에게 검증을 맡긴다.
  • SecurityContextHolder는 기본적으로 ThreadLocal 전략으로 현재 요청 스레드의 SecurityContext를 보관한다.
  • 최신 Servlet Security 흐름에서는 SecurityContext를 읽고 저장하는 책임이 SecurityContextRepository, SecurityContextHolderFilter와 연결된다. 커스텀 인증을 직접 만들면 SecurityContext를 저장해야 하는 흐름까지 확인해야 한다.

헷갈리는 지점

  • Authentication을 로그인 성공 결과로만 보기 쉽다. SecurityContext에서 현재 사용자로 자주 꺼내기 때문이다.
    • 핵심은 인증 전에는 요청 객체이고 인증 후에는 현재 사용자 표현이라는 점이다.
    • 같은 인터페이스가 두 역할을 하므로 isAuthenticated()와 생성 시점을 함께 봐야 한다.
  • Controller가 로그인 성공/실패를 직접 판단해야 한다고 생각하기 쉽다. MVC 방식 로그인에 익숙하기 때문이다.
    • 핵심은 Controller가 자격 증명을 포장할 수는 있지만 검증 책임은 Manager와 Provider로 분리하는 것이다.
    • Controller는 인증 결과를 응답이나 토큰 발급으로 연결하는 위치에 가깝다.
  • SecurityContext가 전역 변수처럼 안전하게 어디서나 유지된다고 오해하기 쉽다. static 접근 형태가 보이기 때문이다.
    • 핵심은 기본 저장 전략이 요청 스레드 중심이라는 점이다.
    • 비동기, 새 스레드, 배치 작업에서는 context 전파를 별도로 고려해야 한다.

확인 질문

  • 인증 전 Authentication과 인증 후 Authentication의 차이는 무엇인가?
    • 인증 전에는 자격 증명을 담은 요청이고, 인증 후에는 principal과 authorities를 가진 현재 사용자 정보다.
  • ProviderManager는 무엇을 하는가?
    • 여러 AuthenticationProvider에게 인증 가능 여부와 검증을 위임한다.
  • SecurityContextHolder에는 무엇이 담기는가?
    • 현재 요청에서 인증된 사용자의 Authentication을 가진 SecurityContext가 담긴다.

Authorization과 권한 규칙

3줄 요약

  • 인증은 “누구인가”를 확인하고, 인가는 “이 요청을 수행할 권한이 있는가”를 판단한다.
  • AuthorizationFilterSecurityContextHolder의 Authentication과 authorizeHttpRequests 규칙을 비교해 접근을 허용하거나 거부한다.
  • 401은 인증 실패 또는 미인증, 403은 인증은 되었지만 권한이 부족한 상태로 나누어 해석한다.

핵심 정리

  • 인증이 성공해야 인가 판단에 사용할 principal과 authority가 생긴다.
  • GrantedAuthority는 role, scope 같은 높은 수준의 권한 표현이다.
  • authorizeHttpRequests는 요청 matcher와 접근 규칙을 순서대로 선언한다. 먼저 매칭되는 규칙이 중요하다.
  • AuthorizationFilter는 기본적으로 Security FilterChain 뒤쪽에서 동작하므로, 직접 추가한 필터가 AuthorizationFilter 앞인지 뒤인지에 따라 인가 대상 여부가 달라질 수 있다.
  • hasRole("USER")는 일반적으로 ROLE_USER authority와 연결된다. prefix 혼동은 403의 흔한 원인이다.
  • Method Security는 URL이 아니라 메서드 실행 권한을 제어한다. 웹 인가와 함께 쓰되 책임을 구분해야 한다.

헷갈리는 지점

  • 로그인만 성공하면 모든 API 접근이 가능하다고 생각하기 쉽다. 인증 완료가 보안 완료처럼 느껴지기 때문이다.
    • 핵심은 인증과 인가는 별개의 단계라는 점이다.
    • 인증된 사용자라도 필요한 authority가 없으면 403이 날 수 있다.
  • hasRolehasAuthority를 같은 것으로 보기 쉽다. 둘 다 권한을 검사하기 때문이다.
    • 핵심은 role 표현에는 ROLE_ prefix 규칙이 개입할 수 있다는 점이다.
    • 저장된 authority 문자열과 matcher에서 기대하는 문자열을 맞춰야 한다.
  • 보안 규칙 순서를 대충 둬도 된다고 생각하기 쉽다. matcher가 여러 개 있으면 모두 고려될 것 같기 때문이다.
    • 핵심은 요청 규칙은 순서와 첫 매칭이 중요하다는 점이다.
    • 넓은 규칙을 먼저 두면 좁은 규칙이 도달하지 못할 수 있다.

확인 질문

  • 인증과 인가의 차이는 무엇인가?
    • 인증은 사용자가 누구인지 확인하는 것이고, 인가는 그 사용자가 특정 요청을 수행할 수 있는지 판단하는 것이다.
  • 401과 403은 어떻게 구분하는가?
    • 401은 인증이 없거나 실패한 경우이고, 403은 인증은 되었지만 권한이 부족한 경우다.
  • authorizeHttpRequests에서 규칙 순서가 중요한 이유는 무엇인가?
    • 요청 matcher가 순서대로 평가되고 먼저 매칭되는 규칙이 적용되기 때문이다.

Session JWT Cookie CSRF

3줄 요약

  • 세션은 서버가 인증 상태를 저장하는 방식이고, JWT는 검증 가능한 토큰 값이며, 쿠키는 값을 브라우저가 자동 운반하는 수단이다.
  • 세션 방식은 Session ID로 서버의 SecurityContext를 복원하고, JWT 방식은 매 요청 토큰 검증으로 Authentication을 다시 만든다.
  • CSRF는 unsafe HTTP method와 브라우저가 인증 정보를 자동 전송하는 성질과 관련되므로 인증 저장 방식과 클라이언트 운반 방식에 따라 대응이 달라진다.

핵심 정리

  • 쿠키는 브라우저가 값을 저장하고 요청에 자동으로 붙여 보내는 운반 수단이다.
  • 토큰은 사용자를 증명하거나 권한 정보를 담은 문자열 값이다. JWT는 토큰 형식 중 하나다.
  • 세션 로그인은 서버 HttpSession에 SecurityContext를 저장하고, 클라이언트는 보통 JSESSIONID 쿠키를 보낸다.
  • JWT Resource Server 방식은 Authorization: Bearer 헤더의 토큰을 검증하고 Authentication을 구성한다.
  • Stateless JWT는 서버 세션 저장 부담을 줄이지만 로그아웃, 강제 만료, 탈취 대응을 위해 refresh token 저장소, denylist, tokenVersion 같은 별도 전략이 필요하다.

헷갈리는 지점

  • JWT와 쿠키를 같은 선택지로 비교하기 쉽다. 둘 다 클라이언트가 들고 다니는 값처럼 보이기 때문이다.
    • 핵심은 JWT는 값의 형식이고 쿠키는 운반 수단이라는 점이다.
    • JWT를 Authorization 헤더로 보낼 수도 있고 쿠키에 담아 보낼 수도 있다.
  • JWT를 쓰면 서버가 아무 상태도 관리하지 않아도 된다고 생각하기 쉽다. stateless라는 설명 때문이다.
    • 핵심은 access token 검증은 stateless할 수 있지만 refresh token, 로그아웃, 탈취 대응은 상태 관리가 필요할 수 있다는 점이다.
    • 만료 전 강제 무효화가 필요하면 서버 측 전략이 필요하다.
  • CSRF는 JWT를 쓰면 항상 필요 없다고 단정하기 쉽다. 세션 쿠키와 함께 설명되는 경우가 많기 때문이다.
    • 핵심은 브라우저가 인증 정보를 자동 전송하는 방식인지 보는 것이다.
    • Authorization 헤더를 JS가 직접 붙이는 구조와 쿠키 자동 전송 구조는 위험 모델이 다르다.
  • CSRF token을 쿠키에 저장하면 쿠키만으로 검증이 끝난다고 생각하기 쉽다. 토큰이 쿠키에 있으니 서버가 알아서 비교할 것처럼 보이기 때문이다.
    • 핵심은 CSRF 방어는 브라우저가 자동 포함하지 않는 요청 위치에 실제 token을 함께 보내 비교하게 하는 것이다.
    • SPA에서는 cookie에 저장된 token을 JavaScript가 읽어 header로 보내는 구성이 자주 쓰인다.

확인 질문

  • 세션, JWT, 쿠키는 각각 무엇에 가까운가?
    • 세션은 서버 저장 방식, JWT는 토큰 값의 형식, 쿠키는 브라우저의 운반 수단에 가깝다.
  • JWT 방식에서 매 요청마다 해야 하는 일은 무엇인가?
    • Bearer token을 검증하고 Authentication을 다시 구성해야 한다.
  • CSRF 대응 여부를 판단할 때 핵심 기준은 무엇인가?
    • 브라우저가 인증 정보를 자동으로 전송하는 구조인지 확인하는 것이다.