1. Spring Security 란?
자바 어플리케이션에서 인증과 인가를 제공하는데 집중된 보안 프레임워크입니다.
🔍 인증과 인가란?
인증(Authentication)은 사용자가 자신의 신원을 확인하기 위해 로그인 등의 과정을 거치는 것이며,
인가(Authorization)은 인증된 사용자가 특정 리소스나 기능에 접근할 수 있는 권한이 있는지 확인하는 과정입니다.
2. Spring Security의 구조
(1) Security Filter Chain
Security Filter Chain는 어플리케이션으로 들어오는 HTTP 요청을
여러 보안 필터들이 보안 검사를 순차적으로 처리하는 일련의 흐름이며,
각 필터는 요청의 특정 보안을 담당합니다.
예를 들어 사용자의 인정 여부를 확인하거나 권한이 있는지 판단하는 역할을 합니다.
이 체인에 포함된 필터는 미리 정의된 순서대로 실행되며,
요청에 포함된 정보(헤더, 쿠키 등)를 분석하여 필요한 보안 검사를 수행하고,
조건에 맞지 않을 경우 요청을 차단합니다.
이렇게 여러 단계의 검증을 거쳐서 어플리케이션을 무단 접근이나 공격으로부터 안전하게 보호할 수 있습니다.
(2) UserDetailsService
UserDetailsService는 Spring Security에서 사용자 인증 과정을 지원하기 위해서
데이터베이스나 다른 저장소에서 사용자 정보를 불러오는 서비스 인터페이스 입니다.
이 인터페이스는 사용자의 이름, 비밀번호, 권한 등의 세부 정보를 제공하여
인증 및 인가 과정에서 필수적인 사용자 데이터를 확보할 수 있도록 합니다.
(3) AuthenticationManager
AuthenticationManager는 Spring Security에서 사용자 인증 요청을 처리하는 핵심 컴포넌트입니다.
이 컴포넌트는 사용자가 입력한 정보를 확인하여, 인증 여부를 결정하며,
또한 여러 인증 프로바이더(데이터베이스 기반 인증, LDAP 인증 등)와 협력하여 다양한 인증 방식을 지원합니다.
인증에 성공하면 인증 토큰을 생성하여 보안 컨텍스트에 저장합니다.
위의 과정을 통해 애플리케이션이 인증된 사용자 정보를 활용할 수 있도록 합니다.
🔍 보안 컨텍스트(Security Context)란?
애플리케이션 내에서 현재 인증된 사용자와 그 권한 정보를 관리하고 저장하는 영역입니다.
보안 컨텍스트는 일반적으로 ThreadLocal에 저장되어,
요청 처리 과정 동안 어플리케이션의 여러 컴포넌트가 현재 사용자의 보안 정보에 쉽게 접근 할 수 있도록 도와줍니다.
(4) PasswordEncoder
PasswordEncoder는 비밀번호 암호화 및 비교 기능을 제공하는 컴포넌트 입니다.
이를 통해 사용자가 입력한 비밀번호를 암호화하여 해시 값으로 변환하여,
데이터베이스에 암호화로 저장된 비밀번호와 비교할 수 있도록 도와줍니다.
3. 기본적인 Security 설정
// 예시로 작성한 것으로 버전마다 다를 수 있다
@Bean
publc SecurityFilterChain securityFilterChain (HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequest(auth -> auth
.requestMatchers("/api/public").permitAll()
.requestMatchers("/api/private").authenticatied()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
🤔 시큐리티 필터 체인에 Security 설정을 하는 이유
시큐리티 필터 체인은 애플리케이션으로 들어오는 모든 HTTP 요청을 가로채어,
미리 정의된 보안 정책에 따라 요청을 검증하고 처리합니다.
따라서 필터 체인에 보안 설정을 구성하면,
각 요청이 들어올 때마다 필요한 인증 및 인가 과정을 일관되게 수행할 수 있어서 전반적인 보안성을 강화할 수 있습니다.
4. Security Filter Chain
(1) Servlet Filter Chain
🔍 서블릿(Servlet)이란?
자바로 작성된 웹 서버의 컴포넌트입니다.
웹 브라우저에서 페이지를 요청하면,
해당 요청이 웹 서버로 전달되어 서블릿이 필요한 작업을 처리한 후
결과를 HTML 페이지나, 다른 형태의 응답으로 만들어 브라우저에 보내는 역할을 합니다.
예를 들어
사용자가 브라우저에서 “회원 정보 보기” 버튼을 클릭하면
해당 요청이 웹 서버로 전송되고, 서블릿이 DB에서 회원 정보를 조회합니다.
이후 조회한 정보를 HTML 형식으로 만들어 응답으로 보내주면
브라우저가 그 결과를 화면에 보여주는 방식입니다.
서블릿 필터 체인은 웹 어플리케이션 내에서 HTTP 요청과 응답을 가로채어 순차적으로 처리하는 필터의 모음입니다.
이 필터들은 보안, 로깅, 인코딩 등 여러 목적을 위해 활용될 수 있습니다.
🤔 Spring Security Filter Chain과 차이점
Spring Security의 필터체인은 서블릿 필터 체인의 일부로 동작합니다.
서블릿 필터 체인은 범용적으로 모든 요청을 처리하지만,
스프링 시큐리티 필터 체인은 보안 관련 요청에 대해 추가적인 검증을 수행합니다.
(2) Spring Security 필터 흐름도
📌 Spring Security 이것만 알아두기!
1. 요청이 들어오면, DelegatingFilterProxy에서 시작하고, FilterChainProxy에서 필터들을 관리
2. 각 필터를 순차적으로 실행하며, 인증과 인가를 수행
3. 최종적으로 요청이 허용되면 Servlet에서 요청을 처리 후 응답을 반환
4. JWT 기반 인증을 적용할 땐, JwtAuthenticationFilter를 추가하여Access Token을 검증
1️⃣ DelegatingFilterProxy
Spring의 어플리케이션 컨텍스트에 정의된 Bean을 대상으로 동작하도록 설계된 특수한 필터입니다.
서블릿 컨테이너가 DelegatingFilterProxy를 실행하면,
이 필터는 내부적으로 Spring Security의 핵심 필터인 FilterChainProxy 빈으로 제어를 위임합니다.
이를 통해 Spring Security의 복잡한 보안 필터 체인을 손쉽게 통합하고 관리할 수 있으며,
Spring의 의존성 주입 및 AOP 같은 기능들을 활용할 수 있게 됩니다.
결국 DelegatingFilterProxy는 서블릿 필터 체인의 시작점 역할을 하면서,
Spring Security의 여러 보안 필터들이 효과적으로 동작하도록 연결해주는 다리 역할을 수행합니다.
2️⃣ FilterChainProxy
Spring Security의 중심 필터 체인으로,
어플리케이션에 들어오는 모든 요청에 대해 일련의 보안 필터들을 관리하고 실행하는 역할을 합니다.
요청이 도착하면 FilterChainProxy는 미리 설정된 WebSecurity 설정에 따라
어떤 보안 필터들이 해당 요청에 적용되어야 하는지 결정하며, 각 필터를 순차적으로 실행합니다.
이러한 방식은 Chain-Of-Responsibility 패턴과 유사하며
각 필터는 인증, 인가, 세션 관리, CSRF 보호 등 특정 보안 작업을 담당합니다.
요청의 보안 검증이 완료되면, 요청은 서블릿으로 전달되어 비즈니스 로직을 처리합니다.
다양한 보안 기능을 체인으로 구성하여 관리함으로써, 어플리케이션의 보안을 체계적이고 효과적으로 관리합니다.
🔍 Chain-Of-Responsibility 패턴이란?
요청을 처리하는 여러 객체들이 일련의 체인 형태로 연결되어,
각 객체가 자신이 처리할 수 있는 요청인지 판단하고 처리하고,
만약 처리할 수 없으면 다음 객체로 요청을 전달하는 디자인 패턴입니다.
3️⃣ SecurityContextPersistenceFilter
사용자가 로그인 하면, 사용자의 정보는 보안 컨텍스트에 저장됩니다.
사용자가 어떤 요청을 보낼 때,
SecurityContextPersistenceFilter 필터는 보안 컨텍스트를 불러와서, 요청을 처리 할 수 있도록 합니다.
요청을 처리한 후, 만약 보안 정보가 변경 되었다면, 이 필터가 정보를 컨텍스트에 다시 저장해 둡니다.
즉, 요청이 시작될 때 보안 정보를 가져오고, 요청이 끝난 후 변경된 보안 정보를 저장하여
사용자의 로그인 상태 같은 보안 정보를 여러 요청 간에 지속적으로 유지할 수 있도록 도와주는 필터입니다.
4️⃣ SessionManagementFilter
Spring Security에서 세션 관련 정책을 적용하는 역할을 합니다.
일반적인 상태 기반 인증(stateful authentication)에서는 사용자의 세션을 관리하며,
세션 고정 보호, 동시 세션 제어, 세션 만료 처리 등 다양한 기능을 제공합니다.
그러나 JWT와 같은 토큰 기반 인증을 사용하는 경우,
서버에서 별도의 세션 정보를 유지할 필요가 없기 때문에, STATELESS로 설정됩니다.
Stateless에서는 각 요청이 독립적으로 처리되며,
인증 정보가 JWT 토큰 자체에 포함되어 전달되므로,
서버는 세션을 생성하거나 유지하지 않습니다.
이로 인해 애플리케이션은 Stateless를 유지하며 확장 가능한 보안 구조를 구현할 수 있습니다.
(3) Spring Security 인증 과정 흐름도
📌 이것만은 꼭 기억하기
1. Spring Security는 아래의 순서로 요청을 처리
AuthenticationFilter → AuthenticationManager → AuthenticationProvider → UserDetailsService
2. 사용자가 인증되면 SecurityContextHolder에 저장하여 이후 요청에서 재사용 가능
3. JWT 기반 인증을 사용할 경우, AuthenticationFilter에서 JWT 검증을 수행하고,
인증이 완료되면 SecurityContext에 저장하여 이후 인증 요청 시 활용
5. JWT와 SpringSecurity
(1) JWT 알아보기
토큰 기반 인증과 JWT: 액세스 토큰과 리프레시 토큰의 핵심 이해
1. 토큰 기반 인증이란?사용자가 서버에 접근할 때, 인증된 사용자인지 확인하는 방법은 크게 두 가지로 나뉜다. (1) 토큰 기반 인증 vs 세션 기반 인증1️⃣ 토큰 기반 인증클라이언트가 토큰을
dev-leonie.tistory.com
1️⃣ JWT 란?
Json Web Token의 약자로, 인증 및 정보 교환을 위한 토큰 기반 인증 방식입니다.
세션 기반 인증과 다르게 서버가 상태를 유지할 필요가 없는 Stateless한 방식으로,
클라이언트는 토큰을 HTTP 헤더에 포함하여 요청시 서버에 전달합니다.
2️⃣ Access Token
Access Token은 사용자가 인증된 상태임을 나타내는 토큰으로, 보통 짧은 만료 시간 (예: 15~30분)을 가집니다.
클라이언트는 요청 시 해당 토큰을 HTTP 해더의 `Authorization: Bearer <Access Token>` 에 포함하여 서버로 전송하고,
서버는 이 토큰을 검증하여 사용자의 인정 정보를 확인합니다.
만약 Access Token이 만료되면 더 이상 사용할 수 없으므로, 새로운 토큰을 재발급 받아야 합니다.
3️⃣ RefreshToken
Refresh Token은 Access Token이 만료되었을 때,
새로운 Access Token을 발급받기 위해 사용되는 토큰입니다.
일반적으로 Access Token보다 긴 유효기간(예: 7일~30일)을 가지며,
보안상의 이유로 서버(DB)에서 관리되는 경우가 많습니다.
또한, Refresh Token을 사용해 새로운 Access Token을 요청하는 전용 API(예: /refresh)를 별도로 구현하여,
사용자가 원활하게 인증 상태를 유지할 수 있도록 합니다.
🤔 JWT는 Stateless인데 왜 Refresh Token을 DB에서 관리할까?
JWT는 기본적으로 Stateless하지만, Refresh Token은 보안상의 이유로 서버(DB)에서 관리하는 경우가 많습니다.
1. Refresh Token의 보안성 고려
Refresh Token은 긴 유효시간을 가지고 있기 때문에, 공격자가 무한히 Access Token을 발급할 가능성이 있습니다.
2. 사용자 로그아웃 처리
사용자가 로그아웃할 때, 서버에 저장된 RefreshToken을 삭제하여 새로운 AccessToken을 발급할 수 없록 할 수 있습니다.
이는 로그아웃 후에도 이전 Refresh Token을 통한 재인증을 막아 보안을 강화할 수 있습니다.
3. 다중기기 지원
각 기기별로 별도의 Refresh Token을 발급 받아, 특정 기기에서만 로그아웃이 가능하도록 할 수 있습니다.
(2) Spring Security의 JWT 인증 및 인가 과정
Spring Security는 전통적으로 세션 기반 인증을 사용하지만,
JWT(Json Web Token)를 활용하면 서버에서 세션 정보를 유지하지 않아도 Stateless 인증이 가능합니다.
이 방식에서는 매 요청마다 Security Filter Chain에 추가된 인증 필터가 JWT를 검증하여,
사용자의 인증 정보를 SecurityContextHolder에 저장합니다.
특히, AuthenticationFilter는 각 요청에 포함된 JWT 토큰을 검사하여
유효한 경우 사용자의 인증 상태를 확인하고, 이를 기반으로 인가 과정을 수행하게 됩니다.