본문 바로가기

FastCampus - 한번에 끝내는 Java|Spring 웹 개발/04-2 시큐리티

Security 02: 전체 구조 - 패스트캠퍼스 챌린지 46일차

"너무 많이 퍼먹이려고 한다"

 

스프링 시큐리티 2장입니다. 먼저 오버뷰로 어떤 구조로 되어있는지 살펴보고, 주요 모듈들에서 주의할 점 등을 살펴봅니다.

[장문의 불만글은 라쿤이 맛있어보인다고 물에 헹구러 챙겨갔어요.] 하지만 이런 볼멘소리를 늘어놓아도 결국 제가 제대로 이해 못 했다는 점은 달라지지 않으니까요 0-<-< 오늘 정리는 좀 힘들겠네요... 걍 개요만 보고 나머지는 내일 복습하면서 실습을 해야할지도 모르겠습니다.

결국 위에 박스쳐놓은 것 중 첫번째거만 정리했습니다.

스프링 시큐리티의 전체 구조

스프링 시큐리티가 어려운 이유 중 하나는 시큐리티의 기능 중 많은 부분이 필터에 있기 때문입니다. 필터가 무엇인지 이해하려면 서블릿에 대해서 알아야 하구요. 서블릿은... 오래된 Java EE, 현재는 Eclipse에서 관리하는 EE4J Jakarta 의 스펙이라서 이걸 이해하려면 전체적인 통찰력을 보여주는 프로젝트의 공식 문서가 아닌 블로그 글 밖에 남지 않기 때문입니다. 그러니까 이런 글 말이죠 :3 스프링의 웹 부분만 깔짝일때는 여기까지 알 필요는 없잖아요?

그래서, 먼저 서블릿과 필터에 대해서 조사해봤습니다. 조사는 해봤지만, 이게 무엇인지 파악할 정도로만 해봤구요. 어떻게 돌아가는지 세세하게 살펴보는게 중요한 건 아니니까요.

전체 구조도: 퍼와요~

일단 강의 자료에서 전체 구조도를 퍼와서 보고갑시다. 필터와 서블릿의 흐름을 파악하고 진행하는 편이 이해하기 좋거든요.

퍼가요~ (출처는 아래 링크)

https://gitlab.com/jongwons.choi/spring-boot-security-lecture/-/blob/master/part1/Lec-1...생략...md

서블릿 & 필터

제가 알기로, 서블릿은 웹 발전 역사의 한 부분을 차지하는 Java EE의 표준 스펙입니다. CGI (Java는 아니지만) => JSP (html 에 Java 코드를 끼워넣기) => Servlet (Java 코드가 HTML을 반환) 순으로 발전했다고 알고있습니다. (이거 학부생때 국비지원교육 들으면서 배웠던 거 같은데.. 학교에서 배웠나?)

소스를 좀 살펴보니, Servlet 에는 request 와 response 가 있습니다. 이거 최근의 웹 프레임워크들에서도 많이 보는 미들웨어 같은 거 아닌가요? 그렇다면 서블릿이 페이지 관점에서의 웹서버에서 request - response 처리 관점의 웹서버로 넘어가는 분기점이라고 봐도 될 거 같네요.

interface Servlet

https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/jakarta/servlet/Servlet.html

A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol.

서블릿입니다. 문서에서는 웹 서버 안에서 돌아가는 작은 Java 프로그램이라네요. 앞서도 얘기했듯이 (다른 언어의 프레임워크들에서 부르는) 미들웨어처럼 request 를 받아서 response 를 돌려주는 동작을 한다고 합니다.

생명 주기로 init -> service -> destroy 가 있고, service 메소드는 void service(ServletRequest req, ServletResponse res) 처럼 생겼습니다. 이 정도면 빼박이죠? 구세대의 요청 처리기입니다. 미들웨어라고는 부르기가 어려운 게, 다음 미들웨어를 호출할 next() 가 없잖아요?

spring의 DispatcherServlet

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html

Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters.

스프링에는 DispatcherServlet 이 있고, 이 친구가 다른 컨트롤러들에게 요청을 나눠준다고 알고있습니다. 굳이 서블릿을 여러 개 만들어서 처리하지 않고, 스프링이 내부에서 요청을 나눠준다는 걸로 보면 Servlet 을 프로그램의 일부가 아닌 프로그램 하나라는 개념으로 받아들이고 있다고 보면 되겠죠. 그러면 Servlet 하나가 프로그램 하나라는 원칙을 벗어나지 않기 위해 굳이 DispatcherServlet 내부에서 나눠서 뿌려준다라고 설명할 수 있으니까, 그러니까 말이 되니까요 (잇 메잌 센스~).

추가로 검색해보니 스프링에서 여러개의 DispatcherServlet 을 쓰는 것도 가능하다고... 하네요. 근데 거기까지 알 필요가 있을까요?? :3

추측: spring boot와 서블릿 컨테이너

서블릿 "컨테이너" 가 있다는 말은, 서블릿을 올려서 구동해주는 바깥쪽 웹서버가 있다는 말이겠죠. boot 에는 서블릿 컨테이너인 tomcat이 포함되어 있으니까... 어... 음... 네. 아무래도 단독 구동이 안 되는 확장기능식 프로그램은 설정해서 실행하기가 불편하니까요. 요점은 서블릿 컨테이너가 스프링을 돌린다는 것 정도가 될까요? (추측이지만 맞을거에요)

interface Filter

https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/jakarta/servlet/Filter.html

A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both. 

이건 문서를 좀 더 봐야합니다. 블로그 본문에는 생략하겠지만요.

필터입니다. 서블릿 컨테이너에 의해 호출됩니다. 앱의 일부분이라기보다는 공용 거름망 같은 관점으로 보나보네요. 실제로 예시에도 인증이나 로킹, 암호화 같은 공통으로 써야할 부분들이 있으니까요. "미들웨어" 같은 범용적인 이름은 아니지만... 만약 미들웨어라는 개념을 쓰는 프레임워크였다면 필터와 서블릿의 역할을 모두 미들웨어가 담당하겠죠. 둘을 구분할 이유가 없으니까...

doFilter() 에 FilterChain 이 있고, chain 에 doFilter() 가 또 있다는 게 눈에 띕니다 (FilterChain 패러미터는 없음).

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

그리고 문서를 보아하니 필터를 집어넣는게 여간 번거로운 일이 아닌가봅니다.

Filters are configured in the deployment descriptor of a web application.

그쵸? 그러니까 스프링 시큐리티가 필터 내부에 유사 필터(DelegatingFilterProxy)를 만들었겠죠.

서블릿 기반이 아닌 경우

Spring Security 문서에 보면 Reactive Applications 라는 섹션이 있고,

Reactive applications work very differently than Servlet Applications.

라고 적혀있습니다. 여기에서 유추해보건데, 리액티브 앱은 서블릿을 쓰지 않는 부류를 말하는게 아닐까 싶습니다. 아, 찾아보니 그건 아니네요. Servlet 3.1+ 이후부터 리액티브 API를 지원하나봅니다. 그래도 얼추 비슷했습니다.

공식 문서를 읽자

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

이제 공식 문서를 읽으면 됩니다. 오, 맙소사, 공식 문서가 매우 자세히 설명하네요. 전초지식도 쌓았겠다 깊은 이해를 갖추는 데 유용하리라고 봅니다. 얼추 추측했던 것들이 들어맞네요. 그림은 공식 문서 보시구요.

필터의 동작

요청 -> [필터, 필터, 필터, 서블릿] 의 구조이고, 이 과정은 서블릿 컨테이너가 주관합니다. 응답은 단 하나의 서블릿만 해줄 수 있고요. 앞에서 추측했던 서블릿이 하나의 앱이다! 라고 생각한다는 킹리적 갓심이 얼추 들어맞지 않습니까?

DelegatingFilterProxy

  • 필터는... 서블릿 컨테이너 구동 전에 사전에 등록되어있어야 하고 변경이 불가능한 모양입니다.
  • 또한, 필터는 스프링에 대해서는 하나도 모르니까 스프링 DI 컨테이너가 관리하는 객체들, 즉 Bean 에 대해서는 전-혀 모르겠죠.

이 두 가지 이유로 스프링이 DelegatingFilterProxy 라는 걸 등록합니다. 시큐리티가 아니라 스프링이요. 프록시라는 이름 답게, 스프링이 관리하는 필터 Bean 들에게 처리를 위임합니다. 이렇게 하면 스프리엥 등록된 Bean 도 사용할 수 있고 동적으로 필터를 바꿀수도 있겠죠?

FilterChainProxy 과 SecurityFilterChain

스프링 시큐리티가 스프링의 DelegatingFilterProxy 에 등록해놓은 Bean 필터가 FilterChainProxy구요, 그건 프록시인 만큼 실제로는 SecurityFilterChain 을 호출합니다. SecurityFilterChain 에는 실제 시큐리티 필터인 SecurityFilter 가 있죠.

  • DelegatingFilterProxy (스프링)
    • FilterChainProxy (스프링 시큐리티)
      (* DelegateFilterProxy 에 등록됨, Bean)
      • SecurityFilterChain (스프링 시큐리티)
        (* FilterChainProxy에 등록됨, Bean
        시큐리티 전용이라 스프링이 관리하는 DelegatingFilterProxy에는 등록되어 있지 않음)
        • SecurityFilter x N개 (스프링 시큐리티)

왜 FilterChainProxy와 SecurityFilterChain 가 분리되어 있어?

이 구조에서는 굳이 FilterChainProxy와 SecurityFilterChain 을 분리할 필요가 있을까? 라는 생각이 들 수 있습니다. 하지만 강의에서 "SecurityFilterChain을 교체한다" 라는 내용이 나왔었거든요. 필터체인을 통으로 교체한다고 생각하면 스프링 시큐리티가 관리하는게 분리되어있는게 좋겠죠.

공식 문서가 출처입니다. (상단의 링크)

봐봐요, 공식 문서에서도 분리하잖아요? 사실 강의 내용에도 있었는데 워낙 빠르게 쓱 하고 지나가서...

공식 문서에서는 두 필터 체인의 필터 개수 차이에도 주목하는데요, 이건 또 이후 강의에서 "Configuration" 을 추가하는 방식으로... 이거 어노테이션 뭐더라. @Order 같은 거도 나왔는데... 아무튼 필터 체인별로 필터 구성이 같을 필요는 없다는 얘기입니다. 심지어 필터 0개도 가능하구요.

예외 처리

ExceptionTranslationFilter 라는 필터가 발생한 두 가지 예외를 HTTP 응답으로 바꿔줍니다.

  • AccessDeniedException
  • AuthenticationException 

아마 이후 강의에 나올 거 같아요.

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스: https://bit.ly/37BpXiC

#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는JavaSpring웹개발마스터초격차패키지Online

나머지는 내일 계속! 흥미로운 주제이긴 하지만 더 이상 시간을 태울 수 없으니까요.

강의에서는 SecurityContextHolder 에 대한 구조도와 언급도 있었습니다...만... 아, 이번에 같이 봤던 다음 강의네요.