[Spring Security] 인증과 인가, 구조부터 이해하자

박상민·2025년 5월 5일
0

Spring Security

목록 보기
4/4
post-thumbnail

Spring Security 인증과 인가, 구조적으로 이해하기

"Spring Security를 단순 설정이 아닌 구조로 이해해보기"


1. Spring Security를 왜 공부해야 할까?

Spring 기반의 웹 애플리케이션을 개발하다 보면 어느 순간 보안이라는 주제를 마주하게 된다.
그리고 대부분은 다음과 같은 방식으로 시작한다.

"Spring Security? 일단 예제 코드 붙여놓고 나중에 이해하자."

처음에는 @EnableWebSecurityconfigure(HttpSecurity http) 같은 코드가 마치 마법처럼 동작하는 것처럼 느껴진다. 실제로는 어떻게 작동하는지 모르지만, 우선 돌아가기만 하면 된다는 생각으로 넘어가게 된다.

그러나 프로젝트가 커지고, 인증 방식이 다양해지며,
역할 기반의 인가 처리나 JWT, OAuth2와 같은 복잡한 기능을 도입하게 되면
Spring Security의 구조를 정확히 이해하지 않고는 더 이상 나아가기 어렵다.


인증과 인가, 왜 헷갈리는가?

많은 개발자들이 AuthenticationAuthorization이라는 용어를 혼용해서 사용한다.
나 또한 처음에는 두 개념이 비슷한 의미라고 생각했다.

용어의미
Authentication사용자가 누구인지 확인하는 과정 (예: 로그인)
Authorization사용자가 특정 기능을 수행할 수 있는 권한이 있는지 검증하는 과정 (예: 관리자 페이지 접근)

Spring Security는 이 두 개념을 명확히 구분하고, 각 단계를 체계적인 구조로 처리한다.
하지만 이 구조는 처음 접했을 때 다소 복잡하게 느껴질 수 있다.


공식 문서 기반으로 정리하게 된 이유

이 글은 Spring Security 공식 문서를 바탕으로,
Servlet 기반 애플리케이션에서 인증(Authentication)과 인가(Authorization)가 어떻게 처리되는지 구조적으로 정리하고자 한다.

설정 방법보다는 처리 흐름과 구조 이해에 초점을 맞춘다.

Spring Security를 처음 접하는 개발자뿐만 아니라,
실제 프로젝트에서 보안 흐름을 커스터마이징하려는 개발자에게도
전체 구조를 한 번쯤은 정리해보는 경험이 도움이 된다고 생각한다.


2. Spring Security 개요

Spring Security는 Spring 기반 애플리케이션에 보안 기능을 제공하는 프레임워크이다.
단순히 로그인/로그아웃 처리를 넘어서,

인증(Authentication)과 인가(Authorization), 세션 관리, 보안 헤더, CSRF 보호

등 다양한 보안 기능을 지원한다.

Spring Security의 가장 큰 특징은 다음과 같다:

  • 선언적 설정이 가능하다 (@Secured, @PreAuthorize 등)
  • Filter 기반의 아키텍처로 유연하게 구성된다
  • 관심사의 분리를 철저히 지킨다 (Authentication과 Authorization이 분리됨)

Spring Security의 철학

공식 문서에 따르면, Spring Security는 다음 철학을 기반으로 설계되었다:

“보안은 애플리케이션 전반에 걸쳐 적용되어야 하며, 명확하게 분리된 책임 구조 안에서 유연하게 동작해야 한다.”

이는 단순한 인증/인가 기능의 구현을 넘어,
보안 기능이 전체 애플리케이션 흐름 속에 자연스럽게 녹아들 수 있도록 설계해야 한다는 의미이다.


인증 vs 인가 다시 정리

Spring Security는 크게 두 가지 축으로 나뉜다.

구분설명
인증 (Authentication)사용자의 신원을 확인한다. 로그인 처리와 밀접하게 연관된다.
인가 (Authorization)인증된 사용자가 어떤 리소스에 접근 가능한지를 판단한다. 권한 체크와 연관된다.

Spring Security는 이 두 과정을 각각의 컴포넌트로 나누어 처리한다.
이를 통해 보안 흐름을 보다 유연하고 확장 가능하게 구성할 수 있다.


전체 보안 흐름 요약

Servlet 기반 Spring 애플리케이션에서 요청이 들어오면
Spring Security는 다음과 같은 흐름으로 동작한다:

  1. FilterChainProxy가 요청을 가로챈다.
  2. 인증이 필요한 경우, UsernamePasswordAuthenticationFilter 등이 동작하여 인증을 수행한다.
  3. 인증이 완료되면 SecurityContext에 인증 정보를 저장한다.
  4. 이후 인가 필터가 Authentication 객체를 기반으로 접근 권한을 확인한다.
  5. 모든 검증이 끝난 후, 컨트롤러에 요청이 전달된다.

이와 같은 구조 덕분에 Spring Security는 단순한 설정을 넘어
실제 서비스 요구에 맞춰 다양한 방식으로 확장할 수 있다.


3. Security Filter Chain이란?

Spring Security의 핵심 구조는 Filter 기반 보안 처리이다.
Servlet 기반 웹 애플리케이션은 요청과 응답을 Filter Chain을 통해 처리하게 되며,
Spring Security는 이 구조를 바탕으로 보안 기능을 구현한다.


Filter 기반 구조의 동작 방식

Spring Security는 FilterChainProxy라는 필터를 등록하여,
HTTP 요청이 들어올 때마다 내부적으로 보안 관련 필터들을 순차적으로 호출한다.
이 필터들의 모음이 바로 Security Filter Chain이다.

요청이 들어오면 다음과 같은 흐름으로 처리된다:

  1. DispatcherServlet이 동작하기 전에
  2. DelegatingFilterProxy가 보안 필터 체인을 가로채고
  3. FilterChainProxy가 등록된 보안 필터들을 실행하며
  4. 그중 일부가 인증(Authentication) 및 인가(Authorization)을 수행한다.

DelegatingFilterProxy의 역할

Spring Security의 필터 체인은 일반 서블릿 필터가 아니다.
Servlet 환경에서는 보안 필터를 DelegatingFilterProxy라는 프록시를 통해 등록한다.

  • 실제 보안 로직은 FilterChainProxy에 정의되어 있으며,
  • DelegatingFilterProxy는 해당 Bean을 위임(Delegate)하는 역할을 한다.

이로 인해 보안 필터 설정을 Spring의 ApplicationContext에서 관리할 수 있게 된다.


SecurityFilterChain 구조 이해

하나의 애플리케이션에는 여러 개의 SecurityFilterChain을 둘 수 있으며,
각 체인은 특정 URL 패턴에 대해 별도의 보안 정책을 설정할 수 있다.

예시:

@Bean
public SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
    return http
        .securityMatcher("/api/**")  // 해당 URL만 적용
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .build();
}
  • /api/** 경로에 대해서만 인증을 요구하는 필터 체인을 정의한 예시

이처럼 Spring Security는 필터 체인을 이용하여
URL 패턴마다 서로 다른 인증/인가 정책을 적용할 수 있는 유연한 구조를 제공한다.


📌 핵심 요약

구성 요소역할
DelegatingFilterProxyServlet 필터로 등록되며, 실제 보안 로직으로 위임
FilterChainProxySpring Security 필터 체인을 관리
SecurityFilterChain특정 URL에 대한 보안 필터 그룹

4. 인증(Authentication) 아키텍처

인증은 사용자가 누구인지 확인하는 과정이다.
Spring Security는 이 인증 과정을 여러 컴포넌트와 필터를 통해 처리한다.
이 장에서는 인증이 어떻게 동작하는지 전체 흐름부터 핵심 구성 요소,
그리고 커스터마이징 포인트까지 단계별로 살펴본다.


4-1. 인증 처리 흐름 한눈에 보기

인증 과정은 보통 로그인 요청을 처리할 때 발생하며,
다음과 같은 흐름으로 진행된다:

  1. 사용자가 로그인 정보를 입력하고 요청을 보낸다.
  2. UsernamePasswordAuthenticationFilter가 요청을 가로챈다.
  3. 필터는 입력값을 UsernamePasswordAuthenticationToken으로 변환한다.
  4. 이 토큰은 AuthenticationManager로 전달된다.
  5. AuthenticationManager는 내부적으로 AuthenticationProvider를 호출한다.
  6. AuthenticationProvider는 실제 인증 로직을 수행한다.
  7. 인증에 성공하면 Authentication 객체가 생성되어 SecurityContext에 저장된다.

이후부터는 이 인증 정보를 바탕으로 인가(Authorization)가 수행된다.


4-2. 핵심 구성 요소 살펴보기

Spring Security의 인증 아키텍처는 다음과 같은 주요 컴포넌트들로 구성된다:

📌 1) UsernamePasswordAuthenticationFilter

  • 로그인 요청을 가로채고, 사용자 입력값을 인증 토큰으로 변환한다.
  • 기본적으로 /login 경로를 처리하지만, URL은 커스터마이징 가능하다.

📌 2) AuthenticationManager

  • 인증 요청을 처리하는 중심 컴포넌트이다.
  • 보통 ProviderManager 구현체가 사용되며,
    여러 개의 AuthenticationProvider를 순차적으로 검사한다.

📌 3) AuthenticationProvider

  • 실제 인증 로직을 담당한다.
  • 사용자의 정보를 조회하고, 비밀번호 검증 등을 수행한다.
  • 여러 종류의 인증 방식을 처리하기 위해 다수 등록이 가능하다.

📌 4) UserDetailsService & UserDetails

  • 사용자 정보를 로드하는 서비스이다.
  • 주로 DB에서 사용자를 조회하며, 반환값은 UserDetails 객체이다.
  • 이 객체에 비밀번호, 권한, 계정 상태 등의 정보가 담긴다.

📌 5) PasswordEncoder

  • 비밀번호 암호화를 처리한다.
  • BCryptPasswordEncoder가 기본 구현체로 자주 사용된다.

4-3. 커스텀 인증 구성은 어디서 어떻게?

기본 인증 구조 외에도, 다양한 실무 상황에서는 다음과 같은 확장이 필요하다:

  • 소셜 로그인(OAuth2)
  • JWT 기반 인증
  • 이메일/휴대폰 인증
  • 사내 전용 인증 로직

이럴 때는 다음과 같은 지점을 커스터마이징하면 된다:

확장 포인트커스터마이징 방법
필터 교체UsernamePasswordAuthenticationFilter를 상속하여 재정의
인증 방식 추가새로운 AuthenticationToken, AuthenticationProvider 구현
사용자 조회 방식 변경UserDetailsService 구현체 수정
암호화 방식 변경PasswordEncoder 교체

실제로는 필터를 추가하거나, Security 설정에서 .addFilterBefore()를 활용해
JWT 인증 필터 등을 등록하는 방식이 많이 사용된다.


핵심 요약

컴포넌트역할
UsernamePasswordAuthenticationFilter로그인 요청을 처리하고 토큰을 생성
AuthenticationManager인증 요청을 분배
AuthenticationProvider실제 인증 로직 수행
UserDetailsService사용자 정보 조회
PasswordEncoder비밀번호 암호화/검증

다음 장에서는 인증이 완료된 사용자가
실제로 리소스에 접근할 수 있도록 제어하는 인가(Authorization) 구조를 살펴본다.

5. 인가(Authorization) 아키텍처

인가(Authorization)는 인증된 사용자가 어떤 리소스에 접근할 수 있는지를 판단하는 과정이다.
Spring Security는 인증 이후에 이 인가 처리를 수행하며,
보통 컨트롤러 진입 직전 또는 메서드 실행 직전에 수행된다.


5-1. 인가의 기본 개념

Spring Security는 인증이 완료된 사용자 정보를
SecurityContext에 저장하고, 이를 통해 인가를 수행한다.

SecurityContextHolder.getContext().getAuthentication()

이 코드를 통해 현재 로그인한 사용자의 정보를 조회할 수 있다.
Authentication 객체에는 사용자의 권한 정보(GrantedAuthority)가 포함되어 있으며, 이 권한을 기준으로 리소스 접근 허용 여부를 판단하게 된다.


5-2. 인가 처리 구조

인가 로직은 보통 다음의 흐름으로 처리된다:

  1. 사용자의 요청이 들어온다.
  2. 요청을 가로챈 보안 필터는 SecurityContext에서 인증 정보를 조회한다.
  3. 인가 관련 필터가 AccessDecisionManager를 호출한다.
  4. AccessDecisionManager는 등록된 AccessDecisionVoter들을 통해 인가 여부를 결정한다.
  5. 인가에 실패하면 AccessDeniedException이 발생한다.

📌 주요 컴포넌트 설명

컴포넌트설명
SecurityContext인증된 사용자의 정보를 담고 있는 컨텍스트
AccessDecisionManager인가 여부를 결정하는 중심 로직
AccessDecisionVoter권한을 기준으로 인가를 “투표” 방식으로 결정하는 객체
ExceptionTranslationFilter인가 실패 시 예외를 처리하고 로그인 페이지 또는 에러 응답 반환

기본 구현체는 대부분 AffirmativeBased이며,
하나라도 허용되면 전체 인가를 통과하는 방식으로 동작한다.


5-3. 인가 방식의 종류

Spring Security에서는 크게 URL 기반 인가메서드 기반 인가로 나눠서 인가 처리를 구성할 수 있다.

1) URL 기반 인가 설정(HttpSecurity)
보안 설정 클래스에서 HttpSecurity를 통해 URL에 대한 권한을 설정할 수 있다.

http
  .authorizeHttpRequests(auth -> auth
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .anyRequest().authenticated()
  );

위 예시처럼 URL 경로마다 권한 조건을 지정할 수 있으며,
모든 요청에 대해 인증 여부 또는 역할 기반 제한을 설정할 수 있다.

2) 메서드 기반 인가 (@Secured, @PreAuthorize)
서비스 또는 컨트롤러 계층에서 어노테이션 기반으로 인가 조건을 명시할 수 있다.

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(UUID userId) { ... }

사용 가능한 어노테이션은 다음과 같다:

어노테이션설명
@Secured("ROLE_ADMIN")단순한 역할 기반 체크 (접두사 ROLE_ 필수)
@PreAuthorize("...")SpEL을 이용한 조건 지정 (메서드 실행 전 검사)
@PostAuthorize("...")메서드 실행 후 검사
@RolesAllowed("ADMIN")JSR-250 기반 권한 체크

이를 사용하려면 @EnableGlobalMethodSecurity 또는 Spring Boot 3.0 이상에서는 @EnableMethodSecurity 어노테이션을 활성화해야 한다.


✅ 핵심 요약

구분설명
인가 정보 저장 위치SecurityContext 내 Authentication.getAuthorities()
인가 처리 구조AccessDecisionManager → AccessDecisionVoter 투표 방식
주요 방식URL 기반 설정, 메서드 기반 어노테이션

6. 인증과 인가 흐름 요약 비교

인증(Authentication)과 인가(Authorization) 두 구조가 전체 요청 흐름 상에서 어떻게 연결되는지, 그리고 실무에서 혼동하기 쉬운 지점이 무엇인지 정리한다.


인증과 인가 전체 요청 흐름

클라이언트 요청
       ↓
[인증 처리 필터]
UsernamePasswordAuthenticationFilter
       ↓
AuthenticationManager → AuthenticationProvider
       ↓
인증 성공 → SecurityContext에 저장
       ↓
[인가 처리 필터]
FilterSecurityInterceptor
       ↓
AccessDecisionManager → AccessDecisionVoter
       ↓
인가 성공 시 컨트롤러 진입

인증과 인가 비교 요약

항목인증 (Authentication)인가 (Authorization)
목적사용자가 누구인지 확인사용자가 특정 리소스에 접근 가능한지 확인
시점로그인 시 1회 수행로그인 이후 모든 요청마다 반복 수행
주요 컴포넌트AuthenticationManager, AuthenticationProvider, UserDetailsServiceAccessDecisionManager, AccessDecisionVoter
정보 저장 위치SecurityContextHolder (Authentication 객체 내부)동일 (Authentication.getAuthorities())
실패 시 결과401 Unauthorized 또는 로그인 페이지 리다이렉트403 Forbidden 또는 AccessDeniedException 발생

⚠️ 실무에서 자주 헷갈리는 지점

  • Authentication에는 사용자 ID, 권한 정보, 인증 여부가 모두 포함되어 있다.
  • 로그인에는 성공했지만, 인가에 실패하면 403 에러가 발생한다.
  • 인증 필터는 주로 UsernamePasswordAuthemticationFilter, 인가 필터는 FilterSecurityInterceptor가 사용된다.
  • JWT나 OAuth2처럼 사용자 정보가 외부에서 주입될 경우, 커스텀 필터에서 SecurityContext를 직접 설정해야 한다.

7. 정리하며

Spring Security는 단순한 인증/인가 프레임워크를 넘어,
웹 애플리케이션 보안의 전반적인 구조를 설계할 수 있도록 돕는 강력한 도구이다.


이번 글에서 정리한 핵심 요약

  • Spring Security는 Filter 기반 구조를 통해 요청 흐름을 제어한다.
  • 인증은 사용자의 신원을 확인하고, 인가는 해당 사용자의 리소스 접근 권한을 판단한다.
  • 인증 흐름에는 UsernamePasswordAuthenticationFilter,
    AuthenticationManager, AuthenticationProvider 등이 사용된다.
  • 인가 흐름은 SecurityContext의 권한 정보와
    AccessDecisionManager, AccessDecisionVoter를 통해 처리된다.
  • URL 단위뿐 아니라 메서드 단위로도 인가 처리가 가능하다.

출처
Spring Security 공식 문서

Spring Blog - Spring Security without WebSecurityConfigurerAdapter
Baeldung - Spring Security Authentication
Baeldung - Spring Security Authorization

0개의 댓글