오늘은 면접대비를 하면서 Spring의 주요기능인 AOP에 대해 공부하기 위해 우선 횡단관심사와 Filter에 대해 정리해보았다.
개발을 하다보면 중복으로 발생하는 공통된 코드가 있다. 위의 그림처럼 로깅, 보안, 트랜잭션관리 등 다수의 모듈에서 반복적으로 나타나는 부분이 존재하는데 이를 횡단 관심사(Cross-cutting Concerns)라고 한다.
스프링에서는 이 횡단관심사를 처리할 수 있는 Filter, Interceptor, AOP가 존재한다. 이 기능들을 통해 횡단 관심사를 공통화해서 사용이 가능해지고, 중복코드를 줄여 유지보수를 쉽게 만든다.
하지만 Filter, Interceptor, AOP는 위의 그림처럼 적용되는 곳을 비롯해 차이점이 존재하는데, 각각의 상세한 기능에 대해 알아보려한다.
Filter는 Dispatcher Servlet(Request를 처리할 적절한 컨트롤러를 찾아 할당)에 Request가 전달 되기 전, 가장 먼저 수행되고 Spring Context 외부에 존재해 톰캣과 같은 웹 컨테이너(Servlet Container)에 의해 관리된다.
일반적으로 스프링과 무관하게 전역적으로 처리해야 하는 작업이 Filter에서 처리된다. 웹어플리케이션의 전체적인 흐름을 가로채 요청 or 응답을 수정하거나 추가 작업을 수행한다.
URL 패턴으로 대상을 구분해서 걸러낸다.
Filter에서는 Request와 Response를 조작할 수 있다.(아예 다른 객체로 변경 가능)
Interceptor, AOP와 다르게 스프링의 기능이 아니라 자바의 기능이다. 자바의 javax.servlet.Filter 인터페이스를 구현한다.
init()
필터 객체를 초기화하고 서비스에 추가하기 위한 메서드. 웹 컨테이너가 init 메서드를 1번 호출해 필터 객체를 초기화하면 이후 요청들이 필터를 통해 처리된다.
doFilter()
URL 패턴에 맞는 모든 HTTP 요청이 Dispatcher Servlet을 거치기 전/후에 이곳에서 처리된다.
destroy()
필터 객체를 제거하고 자원을 반환한다.
Spring Security는 Filter를 기반으로 한 보안 프레임워크다. 개발자는 Spring Security를 사용하여 보안 필터 체인을 설정하고, URL 패턴에 따라 요청을 필터링하여 인증/인가를 구현할 수 있다. Spring Security는 다양한 기능과 확장성을 제공하며, 사용자 인증 및 인가, 세션 관리, 보안 헤더 설정, CSRF 방어, 로그인/로그아웃 처리 등 다양한 보안 작업을 처리할 수 있다.
@RequiredArgsConstructor
@Slf4j
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = jwtUtil.resolveToken(request);
if (token != null) {
if (!jwtUtil.validateToken(token)) {
throw new CustomException(ExceptionType.TOKEN_VALIDATION_EXCEPTION);
}
Claims info = jwtUtil.getUserInfoFromToken(token);
setAuthentication(info.getSubject());
}
filterChain.doFilter(request, response);
}
public void setAuthentication(String email) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = jwtUtil.createAuthentication(email);
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
}