Springboot[스프링부트] Spring Security Basic(5) 동작 과정
Spring Security의 Filter, Provider, Handler를 직접 구현해봤습니다. 이번에는 이러한 동작들이 실행되는 과정을 살펴보겠습니다.

위와 같은 이미지로 Security Basic(0)에서 한 번 다룬 적이 있습니다. 이번엔 어떤 과정을 거치는지 살펴보겠습니다.
저희가 Custom 하게 만든 CustomAuthenticationFilter를 포함해서 여러 가지 필터들이 있습니다. 필터들이 서로 연결돼 다음 필터를 호출하는 형태입니다.
그중에서 FilterChain을 상속받고, ThreadLocal로 ServletRequest, ServletResponse를 ApplicationFilterChain이 있습니다. 이것은 필터들의 시작점이며, 위에서 말씀드린 것처럼 필터를 호출해서 시작하게 됩니다.

아래 코드가 의미하는 것은 한 필터가 여러 번 호출하는 것을 막기 위함입니다.
for(ApplicationFilterConfig filter:filters) { if(filter==filterConfig) { return; } }
저희가 만든 CustomAuthenticationFilter를 포함해서 총 4개의 필터가 등록됩니다.
1. CharacterEncodingFilter
- 필터명 그대로 Request에 대해서 Encoding을 해주는 필터입니다. 해당 필터가 끝나면 다음 필터인 SpringSecurityFilterChain을 호출하게 됩니다.
2. SpringSecurityFilterChain(AbstractAuthenticationProcessingFilter , SecurityContextPersistenceFilter, ConcurrentSessionFilter, UsernamePasswordAuthenticationFilter)
- AbstractAuthenticationProcessingFilter: HTTP 기반 인증 요청의 추상 프로세서로서 인증에 대한 요청이 들어왔을 때 가장 먼저 동작하게 됩니다.
- SecurityContextPersistenceFilter: Request 한 번에 반드시 한 번만 호출되며, SecurityContext라는 녀석을 영속화를 해주는 역할을 합니다. 이 과정을 거치며, 세션에 SecurityContext를 저장하고, 요청 전반에 걸쳐 SecurityContextHolder를 이용해서 SecurityContext에 접근할 수 있게 해주는 역할을 합니다.
- ConcurrentSessionFilter: session은 톰캣과 같은 서블릿 컨테이너에서 제공합니다. 따라서 스프링이 세션을 제어할 수 없습니다. 톰캣이 넘겨주는 세션을 스프링은 sessionInformation이라는 래퍼 객체를 만들어 SessionRegistry에서 관리를 함으로써 톰캣의 세션과 SessionRegistry의 세션이 일치한다고는 할 수 없지만 요청이 들어왔을 때 스프링에서 sessionInformation의 expired를 통해 해당 세션을 스프링 컨테이너로 받아 줄지 말지를 결정할 수 있기 때문에 세션을 관리하는 효과를 낼 수 있습니다. 이런 역할을 하는 필터입니다.
- RequestCacheAwareFilter: 저장된 요청(인증이 성공한 요청)이 캐시되고 현재 요청과 일치하는 경우 해당 요청을 재구성합니다.
-ExceptionTlansrationFilter: 필터체인 내에서 생성되는 모든 예외를 처리하며 인증 성공후에 요청에 대한 정보를 캐싱하는 역할을 해준다.
SessionManagementFilter: 요청을 시작한 이후 사용자가 인증되었음을 감지하고 인증한 경우 구성된 세션을 호출해줍니다.
- UsernamePasswordAuthenticationFilter: AbstractAuthenticationProcessingFilter를 상속받아 인증 관련 정보를 처리하는 역할을 합니다.
3. CustomAuthenticationFilter
- 저희가 만든 필터로서 UsernamePasswordAuthenticationFilter 앞에 두고, 먼저 인증을 처리하는 필터입니다.
4. Tomcat WebSocket Filter
- 서버 구축하는데 필요한 필터입니다.(자세한 사항은 나중에 더 공부하고 추가하겠습니다.)
위와 같은 필터들이 차례로 호출되게 됩니다.
그 과정에서 AbstractSecurityInterceptor가 동작하게 됩니다. 이것은 요청에 대해서 보안 즉 인증을 진행해야 하는 요청인지 판별하고, Authentitcation 인증이 필요할 경우 얻는 과정을 진행합니다.

그렇게 Authenctication이 필요 없는 요청이었다면, 그저 AuthenticationFilter들을 호출하지 않은 채 흘러갑니다. 그리고 /login을 진행할 때 AuthenticationFilter들이 동작해서 Authentication을 얻을 수 있게 해줍니다. 이때 AbstractAuthenticationProcessingFilter이 가장 먼저 동작하게 됩니다.
1. doFilter로 실행되며, 요청에 대한 Authentication을 얻으려 합니다.

1-2. 해당 attemptAuthentication은 원래라면 UsernamePasswordAuthenticationFilter가 해당되겠지만 저희는 이것보다 저희가 만든 CustomAuthticationFilter를 앞에 뒀기 때문에 해당 필터가 동작합니다.

2. 그 후 위에서 봤던 ProviderManager를 상속받아 저희가 만든 동작하게 CustomAuthenticationProvider이 동작됩니다. 해당 동작으로 권한을 가진 UsenamePasswordAuthenticationToken이 완성됩니다.

AbstractAuthenticationProcessingFilter에서 인증에 실패했을 때와 성공했을 때 로직을 호출해줍니다.

인증에 성공했다면 저희가 설정한 CustomLoginSuccessHandler를 통해서 Session을 부여하는 등 후처리를 진행할 수 있습니다.

실패시 FailureHandler가 동작합니다. 저희는 따로 정의하지 않았지만 HttpSecurity에 람다식으로도 Handler를 정의할 수 있습니다. 아래는 로그인 실패 시 /login으로 보내주는 과정을 거칩니다.

RequestCacheAwareFilter가 존재합니다. 해당 부분은 로그인을 성공했을 때 ExceptionTlansrationFilter에서 요청 자체를 캐싱합니다. 그 후 캐싱이 존재하는지 확인한 후 케싱이 존재한다면, Request를 감싸서 인증 정보를 넣어줍니다.

그 후 SessionManagementFilter를 거치는데 해당 필터가 하는 일은 요청을 시작한 이후 사용자가 인증되었음을 감지하고 인증한 경우 구성된 세션을 호출해줍니다.

이렇게해서 대략적인 과정에 대한 필터를 살펴봤습니다. 이외에도 다른 필터들도 있지만 설명은 생략하겠습니다. (이미지로 모든 필터를 첨부하겠습니다.)

정리하기
1. 여러 개의 필터들이 연쇄적으로 호출하게 되며, 인증과 인가를 진행하게 된다.
2. 이때 인증과 인증이 필요한 URL에 해당하는 것들만 인증을 요구할 수 있도록 하는 AbstractSecurityInterceptor가 존재한다.
3. 따라서 전체 순서도는 처음 사진에서 봤듯이 Filter -> AuthenticationManager를 구현한 ProviderManager에서 직접 Custom 한 Providers가 동작하며, 인증을 하고 SecurityContextHolder에 Authentication을 저장합니다.
잘못된 부분에 대해서 지적해주신다면 감사하겠습니다.
'SpringBoot > spring security' 카테고리의 다른 글
SpringBoot [스프링부트] Spring Security Oauth2 (6) Google Token 활용 (0) | 2022.05.25 |
---|---|
Springboot [스프링부트] Spring Sercurity Basic(4) (0) | 2022.02.12 |
SpringBoot [스프링부트] Spring Security Baisc (3) (0) | 2022.01.27 |
SpringBoot [스프링부트] Spring Security Basic(2) (0) | 2022.01.27 |
SpringBoot [스프링 부트] Spring Security Basic (1) 프로젝트 생성 (0) | 2022.01.25 |
댓글
이 글 공유하기
다른 글
-
SpringBoot [스프링부트] Spring Security Oauth2 (6) Google Token 활용
SpringBoot [스프링부트] Spring Security Oauth2 (6) Google Token 활용
2022.05.25이전에는 google에 인증을 마치면 Session을 생성해서 사용자의 인증과 인가를 처리했습니다. 이번에는 google이 제공하는 API인 Access Code와 Access Token으로 서버의 JWT 토큰을 발급하여 인증과 인가를 처리해보겠습니다. 진행하면서 삽질을 많이 했는데 그 부분에 대해서도 작성하겠습니다. 간단한 순서도를 보겠습니다. 1. 사용자가 로그인 요청 2. 서버는 Google API Server의 로그인을 하는 URI로 리디렉션 3. 로그인 성공 시 설정한 redirect-uri로 리디렉션 진행하며, Access Token을 발급받을 수 있는 Authorization code를 쿼리 파라미터 넘겨줌 4. Authorization code를 활용해서 Access Token 발급 요청 … -
Springboot [스프링부트] Spring Sercurity Basic(4)
Springboot [스프링부트] Spring Sercurity Basic(4)
2022.02.12기존에 만들었던 Springboot 프로젝트에 SSL/HTTPS를 적용하겠습니다. 1. SSL certificate 만들기 ( cmd 창을 관리자 권한으로 실행해야 합니다.) java의 jdk가 있는 곳으로 이동합니다. where java // 명령어를 입력하면 java의 디렉터리 위치가 나옵니다. 3번째 디렉터리로 이동합니다. keytool -genkey -alias bootsecurity -storetype PKCS12 -keyalg RSA \ -keysize 2048 -keystore bootsecurity.p12 -validity 365 아래와 같이 값을 입력하고, 실행하시면 됩니다. java 디렉터리에서도 파일이 생성된 것을 확인할 수 있습니다. resources 밑에 위치합니다. 실제 운영할 … -
SpringBoot [스프링부트] Spring Security Baisc (3)
SpringBoot [스프링부트] Spring Security Baisc (3)
2022.01.27전 글에서 인증과 권한에 대해 다뤘고, 같은 권한을 가진 User는 다른 User의 리소스에 접근할 수 있었습니다. Interceptor를 통해서 인가 문제를 해결해보겠습니다. 1. Interceptor란? 인터셉터(Interceptor)는 스프링의 Spring Context(ApplicationContext) 기능입니다. 임의의 URI를 호출 시 DispatcherServlet에서 해당 Controller가 처리되기 전과 후에 발생하는 것을 처리할 수 있습니다. 즉 DispatcherServlet -> Interceptor -> Controller -> Interceptor -> ~~ 로 표현할 수 있습니다. 2. UserController 수정 @PostMapping("/character") publ… -
SpringBoot [스프링부트] Spring Security Basic(2)
SpringBoot [스프링부트] Spring Security Basic(2)
2022.01.27프로젝트 개요 User는 회원 가입을 하고 로그인을 할 수 있다. User의 권한을 가진 사람만 캐릭터를 만들 수 있다. 캐릭터는 여러 개 생성 가능하다. Guest의 권한을 가진 사람은 /user과 /character에 접근이 불가능하다. 위의 요구사항을 토대로 만들겠습니다. 기본 개념들에 대한 설명은 생략하고, Security에 관한 내용만 설명하겠습니다. 1. User @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity @Getter public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Col…
댓글을 사용할 수 없습니다.