일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- spring caching
- spring scheduler
- Redis
- springboot
- springboot3
- 값객체
- armeria
- java17
- springcloud
- 애그리거트
- MSA
- ifkakao
- 개발자
- MongoDB
- mongo
- spring6
- docker
- ddd
- 반버논
- Pay
- webframework
- Kotlin
- Spring
- spring-web
- IDDD
- zuul
- 신입
- Conference
- kakao
- 바운디드컨텍스트
- Today
- Total
Easy Understanding
Spring Security 뼈대 정리(Tomcat에서 Spring 까지의 처리 과정) 본문
1. Security의 목적
Spring Security 모듈은 말 그대로 어플리케이션을 'Secure(안전)'하게 유지시키기 위한 모듈이다.
보통 비밀번호나 키 들을 이용해서 접근을 통제하거나(Authentication),
접근하는 사람의 신분이 User인지 Admin인지 등에 따라 제한된 권한을 주기도 한다.(Authorization)
2. 어떤 기술을 기반으로 하는가
Spring은 일반적으로 '웹' 서버를 만드는 데 사용이 된다.
웹 서버는 일반적으로 웹 요청이 들어왔다가(Request), 원하는 정보를 얻어 나가는 형태(Response)로 동작한다.
Spring Security는 들어오는 중에, 웹 요청을 앞에서 미리 방어하는 식으로 구현되어 있다.
정확하게 이런 식으로 구현되어 있지는 않지만, 대략적으로 위의 그림과 같은 느낌이라고 보면 된다.
1) Tomcat에서 웹 요청을 받아서 그것을 Spring에 처리하도록 보내려고 한다.
2) Spring Security에서 방패처럼 그 요청을 막아선다. 여기에서 요청을 확인 후 Spring으로 보낼지 말지 결정한다.
3) Spring Security에서 승인했다면 Spring에서 로직을 수행하여 사용자에게 반환한다.
이렇게 요청을 미리 확인하고 막는 것을 스프링에서는 'Filter'라는 기술을 이용하여 구현한다.
위의 그림의 방패가 즉 Filter가 되는 것인데, 자세한 것은 아래에서 설명하도록 하겠다.
3. 대략적인 그림
아래의 그림은 Spring Security 공식 문서에 표현되어 있는 Spring Security의 전체적인 아키텍쳐다.
1) 가장 먼저 Client에서 요청을 날리면 그 요청을 Tomcat에서 받는다.
2) Tomcat에는 여러 Filter가 존재하지만, Spring에 작성된 DelegatingFilterProxy를 자신의 Filter 중 하나로 등록한다.
3) DelegatingFilterProxy는 내부적으로 스프링의 Bean인 FilterChianProxy를 사용한다
4) FilterChainProxy는 내부적으로 Filter들의 묶음인 SecurityFilterChain을 사용한다.
5) 이 필터들을 다 거치면 Servlet(정확하게는 DispatcherServlet)으로 요청이 성공적으로 전달된다.
4. 세부 설명
1) DelegatingFilterProxy
가장 먼저 DelegatingFilterProxy 라는 클래스가 있는데, 이건 Tomcat과 같은 인터페이스를 구현한 Filter이다.
그래서 구현은 스프링에 되어있지만, Tomcat에서 이 DelegatingFilterProxy를 자신의 필터로 등록한다.
비유하자면, Spring이 Tomcat에게 제물로 바친 Filter이다.
그래서 DelegatingFilterProxy는 Tomcat의 소유가 되어 Tomcat의 필터로 행동한다.
2) FilterChainProxy
DelegatingFilterProxy는 Tomcat의 소유가 되었기 때문에, 내부적으로 Spring을 잘 아는 Filter가 하나 더 필요하다.
그 징검다리를 하는 역할이 FilterChainProxy이다.
게다가 이건 Spring의 Bean으로도 등록이 되어있다.
이제 실질적인 Filtering은 이 FilterChainProxy에서 처리하게 될 것이다.
추가적으로 다음에 나올 SecurityFilterChain들을 내부에 갖고 있는데,
url등 사용자의 조건에 따라서 어떤 것을 사용할지도 결정할 수 있다.
3) SecurityFilterChain
Tomcat이 겉에서 보기에는 DelegatingFilterProxy 하나 밖에 안 보이겠지만,
이제 안에서는 Spring만의 여러 개의 Filter들이 일하기 시작한다.
스프링에 등록되어 있는 각종 필터들은 이 SecurityFilterChain에 저장이 된다.
(이 SecurityFilterChain의 내부에서는 Filter들을 List<Filter>로 관리하고 있다.)
private final List<Filter> filters;
게다가 위의 그림을 보면 알 수 있지만, url 에 따라서 다른 SecurityFilterChain을 사용하게 된다.
여기에는 다양한 필터들이 설정되어 있다.
예시를 들면 다음과 같은 필터들이 있다.
- CorsFilter(CORS를 담당)
- UsernamePasswordAuthenticationFilter(Authentication을 담당)
- BasicAuthenticationFilter(Authentication을 담당)
- RememberMeAuthenticationFilter(Session, Cookie쪽을 담당)
- FilterSecurityInterceptor(Authorization을 담당)
이제부터 구체적인 Authentication, Authorization 과정이 시작되는 것이다.
정리하자면,
- DelegatingFilterProxy: Spring에서 바치는 Tomcat에 저장될 필터.
- FilterChainProxy: 그 안에 들어가서 실제로 Spring Security를 관리하는 실세
- SecurityFilterChain: FilterChainProxy가 관리하는 Filter들의 모음들
라고 정리해 볼 수 있겠다.
4) Filter가 구현된 방식
추가로 FilterChainProxy에서 SecurityFilterChain들을 동작시키는 방식이 좀 세련되어서 정리해 보려고 한다.
코딩테스트에서만 사용했었던 재귀가 여기에서 사용된다.
모든 Filter 클래스들을 보면 다 내부에 아래와 같은 메서드가 존재한다.
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
재귀적으로 동작시킬 수 있는 핵심은 FilterChain이라는 것 덕분이다.
FilterChain이 doFilter()라는 메서드를 내부적으로 갖고 있기 때문에,
계속 chain을 재사용하면서 내부적으로 이런 식으로 동작을 충분히 시킬 수가 있다.
doFilter(..., FilterChain chain){
chain.doFilter(..., FilterChain chain){
chain.doFilter(..., FilterChain chain){
chain.doFilter(..., FilterChain chain){
chain.doFilter(){
....
}
}
}
}
}
자세한 구현은 FilterChainProxy 내부의 VirtualFilterChain 클래스를 살펴보면 된다.
Debugger를 FilterChainProxy의 doFilter()에 찍어서 재귀 호출을 확인하면 좋다.
5. 다른 언어의 사례
다른 언어도 크게 다르지는 않다.
Node.js 에는 Middleware라는 것이 존재하는데,
이 Middleware의 원리도 request(req)를 요청 전에 확인해서, 다음으로 전달하는 것이다.
이름만 다르지 filter와 크게 개념적으로 다르지는 않다.
var express = require('express');
var app = express();
var myLogger = function (req, res, next) {
console.log('LOGGED');
next();
};
app.use(myLogger);
myLogger 함수가 Spring Security의 doFilter()와 크게 다르지 않은 것을 확인할 수 있다.
6. 마무리
이후에 이루어지는 Authentication, Authorization 과정은 이번 정리에서는 제외했다.
이번 스터디에서는 Spring Security가 Spring Web과 연관하여,
구현이 어떤 방식으로 되나에 대한 궁금증을 해결할 수 있었다.
'Study' 카테고리의 다른 글
(2) DDD의 도메인과 바운디드 컨텍스트 - 개발보다는 설계부터! (3) | 2022.02.12 |
---|---|
(1) DDD란 무엇인가 - 데이터 중심 개발과 도메인 중심 개발 (3) | 2022.02.06 |
Spring Web MVC 코드 분석(Initialization와 DispatcherServlet) (0) | 2021.06.22 |
스프링 Web Servlet 문서 읽고 정리하기 (0) | 2021.06.19 |
Spring IoC 코드 읽어보기 (0) | 2021.06.12 |