[SPRING] 스프링 시큐리티 쉽지 않은데?

wannabeing·7일 전
0

SPRING

목록 보기
12/12
post-thumbnail

✅ 공식문서에서

Spring Security is a framework that provides authentication, authorization, and protection against common attacks. With first class support for securing both imperative and reactive applications, it is the de-facto standard for securing Spring-based applications.

Spring Security는 인증, 인가, 그리고 일반적인 보안 공격에 대한 보호 기능을 제공하는 프레임워크입니다.
명령형(Imperative)과 반응형(Reactive) 애플리케이션 모두를 안정적으로 지원하며,
스프링 기반 애플리케이션 보안을 위한 사실상 표준(de-facto standard)으로 자리잡고 있습니다.

구분설명
명령형(Imperative)일반적인 방식의 프로그래밍으로, 요청이 들어오면 순차적으로 처리하고 응답을 반환함. Spring MVC가 대표적
반응형(Reactive)비동기 & 논블로킹 방식으로, 이벤트 기반으로 데이터를 처리함. 리소스를 효율적으로 사용하며 대규모 트래픽에 유리. Spring WebFlux가 대표적

💡 스프링 시큐리티는

Http Request가 DispatcherServlet으로 도달하기 전 Servlet의 Filter를 기반으로 Request에 대한 인증과 인가를 적용시켜주는 프레임워크이다.

✅ 일반적인 클라이언트 요청 플로우

HTTP 요청 → Tomcat"Filter"DispatcherServlet(FrontController)InterceptorControllerServiceRepository

일반적으로 클라이언트에서 요청을 보내면, DispatcherServlet이 하나의 HttpServeletRequest를 받아서 요청을 처리하고 HttpServletResponse 응답을 클라이언트로 보낸다.
그런데, 하나 이상의 Filter가 포함된다면, 클라이언트에서 보낸 요청이 Servlet으로 전달되기 전에 Filter를 거치게 된다.

❓ Servlet(서블릿)

  • 클라이언트의 요청을 처리하고, 그 결과를 반환하는 자바 웹프로그래밍 기술
  • 작성한 자바파일이 컴파일되어 클래스파일로 변환
  • 클래스파일을 톰캣(Servlet Container)에 등록
  • 클라이언트의 요청이 들어오면, 서블릿 컨테이너가 알맞는 서블릿 메소드 실행

❓ Servlet Container (Tomcat)

  • 웹서버(Apache)와의 통신을 처리
  • Listen/Accept와 같은 통신 과정을 대신 해줌
  • 서블릿(Servlet)의 생명주기를 관리
  • 멀티스레딩 지원

❓ 왜 쓰는걸까?

코드를 직접 작성할 경우 스프링에서 추구하는 IoC/DI 패턴과 같은 확장 패턴을 염두해서 인증/인가 부분을 직접 개발하기는 쉽지 않은데, Spring Security에서는 이와 같은 기능들을 제공해 주기 때문에 개발 작업 효율을 높일 수 있기 때문에 사용한다고 한다.


✅ 스프링 시큐리티 구조

1. 필터

스프링 시큐리티는 서블릿의 필터를 기반으로 동작한다.

필터는 말그대로 ‘필터’의 역할을 한다.
필터에서 요청을 걸러냈을 경우, 필터 내부에서 HttpServletResponse를 만들어 서블릿 대신 응답을 보낼 수 있다.

GET http://localhost:8080/mypage 요청이 있을 때,
이 접근은 인가(권한 체크)를 필요로 한다.
로그인 하지 않으면 볼 수 없어야 하기 떄문이다.
HTTP 요청이 우리 서버(Servlet)에 도착하기 전에 미리 수행해주는(걸러 주는) 역할을 하는 게 필터다.

2. Delegating Filter Proxy

서블릿컨테이너와 스프링컨테이너의 연결다리 역할이다.

HTTP 요청 → Tomcat"Filter"DispatcherServletControllerServiceRepository

요청 흐름을 다시보자.
우리는 Controller에 접근하기 전에 인증과 권한 작업을 수행해야 한다.

💡 중요!

서블릿 컨테이너(톰캣)는 Servlet Filter를 직접 등록하지만,
Spring Bean으로 등록된 객체들(ex. JwtFilter)은 톰캣이 인식할 수 없다.
그래서 직접 만든 필터가 Spring Bean이면, 톰캣은 그걸 모른다.
따라서 스프링 시큐리티에서는 DelegatingFilterProxy라는 서블릿 필터의 구현체를 제공한다.


3. FilterChainProxy

  • 스프링 컨테이너의 진짜 보안 필터이다.
  • Spring Bean에 등록되어 있다.
  • DelegatingFilterProxy를 통해 받은 요청/응답을 SecurityFilterChain에 전달하는 역할을 한다.
  • 우리가 Spring Bean으로 등록한 필터들을 관리하는 역할을 한다.
[DelegatingFilterProxy (서블릿 필터)]
        ↓
[FilterChainProxy (Spring Bean)]
        ↓
[SecurityFilterChain0] → 필터 A, 필터 B, 필터 C
[SecurityFilterChain1] → 필터 A, 필터 D ...

4. SecurityFilterChain

  • SecurityFilterChain = 필터들의 묶음
  • Spring Security가 요청에 따라 실행할 "보안 필터 리스트"를 담고 있는 인터페이스이다.
/api/** 요청 → [JWT 인증 필터, 권한 필터, CSRF 필터]

/login 요청 → [UsernamePasswordAuthenticationFilter, CSRF 필터]

즉, URL 패턴에 따라 다른 필터 묶음(=SecurityFilterChain) 을 적용할 수 있도록 도와주는 역할을 한다.

💡 Security Filter의 실행 순서는 SecurityFilterChain에서 정할 수 있다.

public class SecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
    	.addFilterBefore(new JwtFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class)

         ...

CustomFilter(JwtFilter)를 UsernamePasswordAuthenticationFilter 앞에서 실행되게 하는 코드이다.


✅ 정리하자면

사용자 요청
 ↓
[서블릿 컨테이너] // 톰캣DelegatingFilterProxy // 서블릿 컨테이너 & 스프링 컨테이너 연결다리FilterChainProxy // 여러 SecurityFilterChain 관리 및 어떤걸 실행할지 결정SpringFilterChain // 특정 URL에 대해 적용할 필터들의 목록과 순서 보유Spring Security Filters // 실제 우리가 구현한 필터들 동작DispatcherServletControllerServiceRepository

기본 필터 순서

  • 필터에는 기본적으로 순서가 있다.
  • 필터들은 기본적으로 모두 구현되어있다.
  • 우리가 자주 사용하는 필터는 아래와 같이 있다.
    • UsernamePasswordAuthenticationFilter: 폼 로그인
    • BearerTokenAuthenticationFilter: JWT 토큰
    • OAuth2LoginAuthenticationFilter: OAuth 인증
    • AbstractAuthenticationProcessingFilter: 인증필터들의 부모

스프링 시큐리티 인증관련 구조

  • 2️⃣ ID/PW 정보로 UsernamePasswordAuthenticationToken 생성
  • 3️⃣ 이를 AuthenticationManager에게 넘김
  • 4️⃣~8️⃣ 내부적으로 AuthenticationProviderUserDetailsServiceUser 객체 조회
  • 9️⃣ 인증 결과를 다시 필터로 전달
  • 🔟 인증 성공 시 SecurityContextHolder에 인증 정보 저장

JwtFilter(CustomFilter)를 쓴다면

Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);

SecurityContextHolder에 인증객체를 직접 넣어주면된다.

✅ 우리의 최종 목표는 인증/인가를 통해 사용자 정보를 "SecurityContextHolder"에 저장하고, ThreadLocal을 통해 전역에서 공유/참조할 수 있도록 하는 것!


출처

내배캠 튜터님들 및 특강자료
[스프링 공식문서] 스프링 시큐리티 6.4.5
[스프링 공식문서] 스프링 시큐리티 5.4.2
[이랜서 개발블로그] 스프링 시큐리티란?
스프링 시큐리티 동작과정에 대해 알아보자
스프링 시큐리티 구조와 동작원리
서블릿과 서블릿 컨테이너1
서블릿과 서블릿 컨테이너2

profile
wannabe---ing

0개의 댓글