TIL
[TIL] 230531 <Spring> Filter
- -
[일반적인 인증 과정]
[Filter]
- 일반적인 자바 웹 애플리케이션에서 클라이언트(사용자의 요청)는 HTTP나 HTTPS 프로토콜을 사용해서 서버의 자원에 접근하고
클라이언트(사용자) 요청은 서버의 서블릿에서 처리함 - 서블릿은 HTTP 요청을 받아 처리한 후 HTTP 응답을 클라이언트에게 반환함
- 일정을 등록하고 id를 반환
- 일정을 조회해서 반환
- 스프링은 DispatcherServlet이 서블릿 역할을 담당하고 모든 요청을 처리함
- 요청-응답 처리과정 중, 중요한 역할을 하는 주요 컴포넌트가 "필터"임
- 아래의 그림과 같이 서블릿 앞에 위치해 요청과 응답을 가로채서 변경가능
- 한 개 이상의 필터는 필터 체인으로 구성 되어 있으며, 필터 체인에 속한 모든 필터는 요청/응답을 가로채서 변경가능
특징
- Spring에서 모든 요청을 하나의 서블릿인 DispatcherServlet 에서 처리하는 것처럼
하나의 특별한 필터인 DelegatingFilterProxy에 의해 활성화됨 - DelegatingFilterProxy는 스프링 부트의 자동 구성으로 컨테이너에 등록되고 모든 요청을 가로챔
Filter 인터페이스
package jakarta.servlet;
import java.io.IOException;
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
Filter의 라이프 사이클
- init()
- 서블릿이 필터를 등록하는 초기화 과정에서 호출
- doFilter()
- 필터 기능을 하는 메소드
- 요청, 응답, FilterChain 객체에 접근할 수 있음
- FilterChain 객체는 다음 필터를 호출
- destory()
- 서블릿 컨테이너가 필터를 제거할 때 호출
사용 예시 (LoggingFilter)
package com.sparta.springauth.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j(topic = "LoggingFilter")
@Component
@Order(1)
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 전처리
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String url = httpServletRequest.getRequestURI();
log.info(url);
chain.doFilter(request, response); // 다음 Filter 로 이동
// 후처리
log.info("비즈니스 로직 완료");
}
}
FilterChain
- 필터 체인은 말그대로 연쇄적으로 거쳐서 흘러가게 드는 역할
- 필터는 자신의 작업이 완료되면 FilterChain을 호출해서 요청이 다음 필터를 통과하게 만듦
- 단일 책임 원칙!!
- 책임 연쇄 패턴!!!!
▶ 책임 연쇄 패턴
각각의 인스턴스의 책임이 체인처럼 연쇄되어 있음
잠재적 수신자의 동적 체인을 따라 수신자 중 하나에 의해 요청이 처리될 때까지 요청을 순차적으로 전달
장점
- 요청을 체인 아래로 더 이상 전달하지 않고 추가 처리를 사실상 중지하는 결정 내릴 수 있음
- 요청의 처리 순서를 제어 가능
- 단일 책임 원칙 → 작업을 호출하는 클래스들을 작업을 수행하는 클래스와 분리가능
- 개방/폐쇄 원칙 → 기존 클라이언트 코드를 손상하지 않고 새 핸들러들을 도입가능
FilterChain 인터페이스
package jakarta.servlet;
import java.io.IOException;
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
ㄴ Filter의 doFilter() 메소드 내부에 filterChain의 doFilter 메소드를 무조건 호출해야함!
[스프링에서의 Filter]
- Spring Security는 두가지 필터인 DelegatingFilterProxy 와 FilterChainProxy 를 사용
ㄴ 두 필터는 스프링 시큐리티 인프라 스트럭처를 통과하게 만드는 진입점 역할 - SecurityFilterChain 인터페이스도 존재
DelegatingFilterProxy
- 스프링 시큐리티는 DelegatingFilterProxy를 통해 서블릿 필터와 스프링과의 간극을 줄임
- DelegatingFilterProxy는 동일하게 서블릿 필터이며 서블릿 컨테이너가 관리
ㄴ 서블릿 컨테이너가 관리하는 Filter 구현체는 스프링 프레임 워크의 기능을 활용할 수 없음!!!- 별도의 Filter 구현체를 하나 더 정의해 빈으로 등록
- 이 Bean을 DelegatingFilterProxy 안에서 대리인 역할을 하는 delegate(대리자)로 설정
- 즉, DelegatingFilterProxy는 서블릿 필터로서 런타임으로부터 가로챈 요청을 delegate인 bean에 위임해서 처리함!!!!!
FilterChainProxy
- 위에서 말한 것처럼 Bean에 등록되어 대리인 역할을 하는 Filter InterFace의 구현체!!
- 하나 이상의 SecurityFilterChain 을 가질 수 있음
- DelegatingFilterProxy가 가로챈 요청을 FilterChainProxy에서 SecurityFilterChain에 요청을 흘려 보내 필터를 통과하게 만듦
SecurityFilterChain
- 해당 인터페이스는 matches() 와 getFilters() 메소드를 가지고 있다.
- matches()
- SecurityFilterChain 구현체가 요청을 처리하는데 적합한지를 판별
- 스프링 시큐리티는 RequestMatcher 인터페이스와 여러 구현체를 제공
- AnyRequestMatcher를 사용하면 모든 HTTP 요청이 해당 SecurityFilterChain 구현체를 통과
- Url 기반으로 판별하도록 AntPathRequestMatcher도 제공
- getFilters()
- matches()에서 true를 반환하면 SecurityFilterChain에 있는 모든 필터를 거침
- 스프링 시큐리티의 기본 설정을 한다면 SecurityFilterChain 구현체인 DefaultSecurityFilterChain 이 사용됨
[Filter 를 등록하는 방법]
1. @Configuration
- 설정을 위한 별개의 파일(@Configuration이 붙은 객체)이 필요
- setOrder()를 통해 순서를 지정 가능
- addUrlPatterns()을 통해 베이스 URL 및 Whitelist를 설정가능
- 직접 Filter를 Bean으로 등록하여 컨트롤 할 수 있다는 점에서 가장 관리측면에서 편리
@Configuration
public class FilterConfiguration {
/**
* 로그 관련 필터 추가.
*
* @return
*/
@Bean
public FilterRegistrationBean loggingFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoggingFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
2. @Component
- 설정을 위한 별개의 파일이 필요없음
- @Order 애노테이션을 이용해 순서설정가능
- 기본 URL Pattern이 /* 이며 설정불가
@Slf4j
@Component // 추가된 두 가지
@Order(1) // 추가된 두 가지
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
// 전처리
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String url = httpServletRequest.getRequestURI();
log.info(url);
filterChain.doFilter(servletRequest, servletResponse); // 다음 Filter 로 이동
}
}
3. @WebFilter + @ServletComponentScan
- 설정을 위한 별개의 파일이 필요없음
- 대신, 애플리케이션 실행되는 메인 객체위에 @ServletComponentScan을 사용해야함
- @Order를 이용한 순서 등록을 사용불가
- 각 필터에 대한 순서를 보장불가
- @WebFilter의 value나 urlPatterns 파라미터를 이용해 whitelist 방식으로 베이스 URL을 설정가능
- @WebFilter("/filter/*")
- @WebFilter({"/login", "/items"})
- @WebFilter(urlPatterns = "/filter/*")
- @WebFilter(urlPatterns = {"/login", "/items"})
@Slf4j
@WebFilter
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
// 전처리
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String url = httpServletRequest.getRequestURI();
log.info(url);
filterChain.doFilter(servletRequest, servletResponse); // 다음 Filter 로 이동
}
}
@ServletComponentScan
@SpringBootApplication
public class FilterPatternApplication {
/**
* main.
*
* @param args argument.
*/
public static void main(String[] args) {
SpringApplication.run(FilterPatternApplication.class, args);
}
}
4. @WebFilter + @Component
- 2번과 3번을 모두 사용
- 설정을 위한 별개의 파일이 필요없음
- 컴포넌트 스캔 방식을 사용하기 때문에 @ServletComponentScan도 필요없음!
- @Order 애노테이션을 이용해 순서를 설정가능
- @WebFilter의 value나 urlPatterns 파라미터를 이용해 베이스 URL이나 Whitelist 방식으로 설정가능
- @WebFilter("/filter/*")
- @WebFilter({"/login", "/items"})
- @WebFilter(urlPatterns = "/filter/*")
- @WebFilter(urlPatterns = {"/login", "/items"})
- 어노테이션이 기본으로 3개가 필요
@Slf4j
@WebFilter
@Component
@Order(1)
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
// 전처리
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String url = httpServletRequest.getRequestURI();
log.info(url);
filterChain.doFilter(servletRequest, servletResponse); // 다음 Filter 로 이동
}
}
'TIL' 카테고리의 다른 글
[TIL] 230604 <Spring> Spring Security 동작원리 (1) | 2024.06.04 |
---|---|
[TIL] 230603 <Spring> 회원가입, 로그인 기능이 있는 투두앱 백엔드 서버 만들기 (3) (1) | 2024.06.03 |
[TIL] 230530 <Spring> 회원가입, 로그인 기능이 있는 투두앱 백엔드 서버 만들기 (2) (0) | 2024.05.30 |
[TIL] 230529 <Spring> 회원가입, 로그인 기능이 있는 투두앱 백엔드 서버 만들기 (1) (0) | 2024.05.29 |
[TIL] 230528 <Spring> My Select Shop (0) | 2024.05.28 |
Contents
소중한 공감 감사합니다