본문 바로가기
Spring

사용자 요청부터 스프링 시큐리티까지 feat)OAuth2

by ernest45 2025. 4. 14.

 

스프링 시큐리티 6 기준!

 

 

 

 

 

 

실제 스프링 시큐리티를 만지면서 너무 어렵고 깊이있게 파고들고 싶어서 좀 더 알아보는 흐름을 갖고 싶어서 써보겠다

 

실제 유저가 스프링에 요청을 보내는 순간으로 부터 시작해서 스프링 시큐리티 작동 원리를 찾아보고

일반적인 form 로그인과 Oauth의 경우까지 알아보자

 

 

 

 

1. http 요청 시 흐름

 

 

Client가 API 요청을 하면

Web Application server(Java에서는 Tomcat) → Servlet(Java에서는 Dispatcher Servlet ) → Controller 

순서로 요청이 전달되는데,

 

 Filter chain Web Application server Servlet 사이에서 작동한다.

 

// 흐름
HTTP 요청 → WAS → 필터 → 서블릿 → 컨트롤러

// 체인
HTTP 요청 → WAS → 필터 → 필터2 → 필터3 → 서블릿 → 컨트롤러

 


(톰캣과 Dispatcher Servlet사이에 존재하는 필터체인)

 

 

 

 

2. FilterChain

 
Servlet은 클라이언트 요청을 Dispatcher Servlet에 가져다 주기 전 여러 필터를 거치게 되는데
 
그 필터들의 연결이 **FilterChain**

 

**필터체인**은 요청을 처리하기 전에 미리 정해진 필터 통과. 

 

필터는 여러 개가 체인처럼 연결돼 있어서, 요청이 하나씩 통과하면서 점검받는 구조(순서대로가 중요)

각 필터는 doFilter 메서드를 통해 요청을 받아서 뭔가를 하고, 다음 필터나 서블릿으로 넘김.

 

(그래서 모든 필터를 호출 후 doFilter을 메서드를 호출해 넘겼어야 했다)

 

 

 

 

 

3. 스프링 시큐리티 실제 흐름

 

 

 
 
 
 

흐름도

 
로깅 필터 -> 인코딩 필터 -> DelegatingFilterProxy -> 커스텀 필터
 
 
 
스프링 시큐리티를 설정을 했다면 서블릿 컨테이너에서 각필터가 진행되다가
 
 
DelegatingFilterProxy가 호출될 때 스프링 컨텍스트에 있는 FilterChainProxy로 작업을 넘김

 

쉽게 말하면, DelegatingFilterProxy는 호출만 하고,

실제 보안 로직은 FilterChainProxy가 여러 필터를 불러서 처리!!

 

적절한 인증/인가 작업 후 다시 커스텀필터를 호출 후 최종  Dispatcher Servlet mvc 동작!

 

 

 

 

 

4. 스프링 시큐리티 필터 종류들(FilterChainProxy)

FilterChainProxy는

위 종류들을 갖고 있는 springSecurityFilter들을 요청에 맞게 호출함

 

 

일반 세션 폼 로그인 기반 시   springSecurityFilter 중요한 필터 흐름 

 

SecurityContextPersistenceFilter

  • 이전 요청에서 저장된 SecurityContext 복원(세션에서 인증 정보 가져오기) or
  • SecurityContextHolder 생애 주기를 관리(실제 인증빼고)
  • 호출 후 응답까지의 로컬쓰레드에 해제까지의 전반적인 흐름의 시작과 끝에 있음 

try- context 저장,  finally- 쓰레드로컬에서 해제

 

-세션 기반이라,  jwt 쓸꺼면 상관없다.

 

 

 

 

 

security 5.7 이후로  @deprecated됨

여기에 대해서도 조금 쓸 일이 많은 것 같긴 하다..

무조건 세션에 저장을 안해도 된다는 뜻인 거 같음

https://www.inflearn.com/community/questions/883957/5-7-%EC%9D%B4%ED%9B%84%EB%A1%9C-deprecated-%EB%90%98%EC%97%88%EC%8A%B5%EB%8B%88%EB%8B%A4?srsltid=AfmBOop5w8r2NhU11uA5kbkr1gEOWWLghYjyYRjdTtZRq6m1QiT7epYs

 

 

 

UsernamePasswordAuthenticationFilter ( 실제 핵심 로그인처리)

  • 로그인 폼에서 아이디/비밀번호를 받아 인증 처리
  • authenticaiton에 실제로 인증을 요청하는 핵심

AuthenticationManager, AuthenticationProvider

 

BasicAuthenticationFilter

  • HTTP Basic 인증(헤더에 인증 정보 넣는 방식) 처리.

 

AnonymousAuthenticationFilter

  • 로그인 안 한 사용자를 "익명 사용자"로 설정.

ExceptionTranslationFilter

  • 보안 예외(401, 403 등) 처리.
 

FilterSecurityInterceptor

  • 최종 권한 체크.
 
 
** 필터는 순서가 중요하다. 인증 후에 인가해야하기에 **
 
 
@EnableWebSecurity(debug = true)
config 설정 파일에 @EnableWebSecurity(debug = true)를 할 경우 필터체인 순서를 볼 수 있음!
무조건 개발환경에서만 해야한다!
 

 

 

 

 

5. OAuth 의 경우

 

 

OAuth2AuthorizationRequestRedirectFilter

 

 

특정 uri에서 호출됨  :/oauth2/authorization/{provider}   

 

 ex) 구글의 예시: 

/oauth2/authorization/google

  

OAuth2 로그인 요청을 받아 사용자를 외부 제공자의 로그인 페이지로 리다이렉트시키는 필터.

 

 

 

순서도

 

1. 로그인 요청 시  인증 요청(Authorization Request)을 만듦

 

2 .Authorization Request에는 (client_id,redirect_id, 스코프 등)이 포함

 

3. 사용자를 제공자의 로그인 페이지로 리다이렉트 (예: 구글 로그인 화면).

 

 

결과 !

 

사용자가 구글 같은 제공자에서 로그인하면 인가 코드(authorization code)가 리다이렉트 URI로 돌아온다. 이 코드는 다음 필터가 처리!

 

 


 

 

 

OAuth2LoginAuthenticationFilter 

 

위에서 받은 인가 코드를 처리해 사용자 인증을 완료하고 SecurityContextHolder에 인증 정보를 설정하는 필터

 

특정 uri에서 호출됨 /login/oauth2/code/*

 

ex) 구글의 예시:  /login/oauth2/code/google 

(내가 지정한 redirectURL)

 

 

1. 위 코드에서 받아온 인증코드로 구글에게 제공자 정보에 접근 가능한 액세스 토큰 발행

 

2. 액세스 토큰으로 사용자 실제 정보 받음

 

    OAuth2UserCustomService가 이 데이터를 처리 (예: DB 저장).

 

블로그 만들기 -Oauth2구글에 요청이 자동으로 간다고?

(실제 실행 메서드가 궁금하면  보고오자)

 

 

3. OAuth2AuthenticationToken을 만들어 SecurityContextHolder에 저장.

 

4.성공oAuth2SuccessHandler() 호출, 실패oAuth2failureHandler() 호출

 

 

결과 !

인증 성공 시 SecurityContextHolder에 사용자 정보가 채워지고, 내 핸들러가 JWT를 발급해 클라이언트에 준다. 이후 요청은 TokenAuthenticationFilter가 JWT로 인증 유지!

 

 

 

usernamePasswordauthenticationfilter의 인증 과정을 거침

 

 

 

 

 

위 두 필터가 OAuth의 핵심이다.
 
 

 

스프링 시큐리티의 기본 필터체인에는 포함되어 있지 않음

필터들은 OAuth 2.0 로그인 기능을 지원하기 위해 별도로 추가된 필터라서

 "spring-security-oauth2-client" 의존성 추가가 필수다.

 

 

OAuth의 흐름도

  1. 요청 → DelegatingFilterProxyFilterChainProxy.
  2. OAuth2AuthorizationRequestRedirectFilter 후 OAuth2LoginAuthenticationFilter로 인증 처리.
  3. SecurityContext에 인증 정보 저장 → 디스패처 서블릿.

 

 

 

 

 

 

-- 질문 -- 미완

HttpSessionOAuth2AuthorizationRequestRepository가 필요한 이유를 따라가다

redirect 때문인 것을 알게 되었는데 그럼 왜 redirect 방식으로 oauth를 채택했나 궁금해졌다!

 

 

 

 

인가 요청 저장소의 역할:
세션이나 쿠키에 저장하는 건, 리다이렉트로 인해 HTTP 요청이 끊기니까 흐름을 이어가기 위한 필수 장치. AuthorizationRequestRepository(예: HttpSessionOAuth2AuthorizationRequestRepository)가 이 역할!
 
난 jwt를 사용하기에 이 세션이 아닌 쿠키방식의 저장소를 커스텀했다
 

3.4 서버의 멀티스레드 처리
웹 서버(WAS, 예: Tomcat)는 멀티스레드로 여러 클라이언트의 요청을 동시에 처리해. 네가 인용한 문맥에서 "서버는 아무것도 할 게 없지"라는 건, 특정 클라이언트의 OAuth 흐름에 대해선 콜백 요청이 올 때까지 기다리는 거지, 서버 전체가 멈춘다는 뜻은 아니야. 리다이렉트를 보내면:
  • 서버는 그 클라이언트의 요청을 끝내고 스레드를 해제해.
  • 다른 클라이언트의 요청(예: 다른 사용자의 페이지 조회, API 호출)을 계속 처리해.
  • 콜백 요청이 오면 새로운 스레드로 처리 시작.
반면, 서버가 기다리려면 스레드가 계속 점유된 상태로 대기해야 해. 이건 서버의 확장성(scalability)을 망가뜨리고, 동시 접속자가 많아질수록 성능이 급격히 떨어져.
 
5. OAuth의 리다이렉트 설계가 더 나은 이유
OAuth 2.0이 리다이렉트를 선택한 건 우연이 아니야. 이 방식이 "기다리는" 방식보다 나은 이유를 정리해보면:
  • 효율성: 서버는 리다이렉트를 보낸 후 리소스를 해제하고 다른 요청을 처리해. 콜백 요청이 올 때만 다시 처리하면 되니까 확장성이 좋아.
  • 보안: 인가 요청을 세션이나 쿠키에 저장하고, state 파라미터로 CSRF 공격을 방지해. 기다리는 방식은 상태를 유지하기 어렵거나 새로운 보안 위험이 생길 수 있어.
  • 사용자 경험: 사용자는 익숙한 제공자 UI(구글 로그인 화면)에서 인증하고, 애플리케이션으로 자연스럽게 돌아와. 기다리는 방식은 사용자에게 "로딩 중" 같은 부자연스러운 UI를 보여줄 가능성이 커.
  • 표준화: OAuth 2.0은 전 세계적으로 표준화된 프로토콜이라, 구글, 페이스북, 깃허브 등 모든 제공자가 리다이렉트 기반으로 동작해. 기다리는 방식은 표준에서 벗어나서 호환성 문제 생김.

 

 

 

 

 

 

 

 

https://docs.spring.io/spring-security/reference/servlet/architecture.html

 

Architecture :: Spring Security

The Security Filters are inserted into the FilterChainProxy with the SecurityFilterChain API. Those filters can be used for a number of different purposes, like exploit protection,authentication, authorization, and more. The filters are executed in a speci

docs.spring.io

https://velog.io/@zini9188/Spring-Security-Filter%EC%99%80-FilterChain

 

[Spring Security] Filter와 FilterChain

서블릿 필터는 서블릿 기반 애플리케이션의 엔드포인트에 요청이 도달하기 전에 중간에 요청을 가로챈 후 어떤 처리를 할 수 있도록 해주는 JAVA의 컴포넌트필터의 처리가 완료되면 디스패처서

velog.io

 https://roadj.tistory.com/15 

https://www.baeldung.com/spring-mvc-handlerinterceptor-vs-filter

https://www.youtube.com/watch?v=02PavC_ZzQA

'Spring' 카테고리의 다른 글

JPA 1+N 문제 실습  (0) 2025.04.12
@transactional을 private메서드에 사용하고 싶다  (0) 2024.01.21
@entity 빨간줄  (0) 2024.01.16