12-4. (실습) 스프링 V3 (인증 - 필터, 인터셉터)

박은서's avatar
Feb 24, 2026
12-4. (실습) 스프링 V3 (인증 - 필터, 인터셉터)
notion image

1. 인증 (필터)

※ 필터(Filter)

1) 필터(Filter)의 특징과 단점 정리

① 필터는 DispatcherServlet 앞에서 동작
  • Spring MVC 요청 흐름
    • Client → Filter → DispatcherServlet → Controller
      ➡️ 필터는 DispatcherServlet보다 먼저 실행됨
② 예외를 DispatcherServlet으로 넘길 수 없음
  • 필터에서 예외 발생 시
    • throw new Exception() → DispatcherServlet에서 캐치 불가
  • 이유
    • DispatcherServlet에 도달하기 전에 필터에서 이미 요청 처리됨
    • Spring의 @ControllerAdvice, @ExceptionHandler 적용 안 됨
③ 필터에서 예외 직접 처리 필요
  • 필터 내부에서 처리 방식
    • try-catch 사용
    • 직접 Response 작성
  • 예시 패턴
    • try { // 인증 체크 } catch (Exception e) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Unauthorized"); return; }

2) 필터에서 인증 처리 시 고려사항

① 인증 실패 시 DispatcherServlet 진입 차단이 정상
  • 핵심 개념
    • 인증/인가 실패 → 비즈니스 로직 진입 자체가 잘못
    • DispatcherServlet → Controller까지 갈 필요 없음
➡️ 필터에서 요청을 차단하는 것이 맞는 설계
② 필터는 "입구 차단기" 역할
  • 필터의 책임
    • 요청 전처리
    • 인증 / 인가 검사
    • 공통 보안 처리
  • DispatcherServlet의 책임
    • 핸들러 매핑
    • 컨트롤러 실행
    • 예외 처리

3) 필터 vs 인터셉터 vs 컨트롤러 예외 처리

구분
필터
인터셉터
컨트롤러
실행 위치
DispatcherServlet 이전
이후
내부
Spring 예외 처리 사용
❌ 불가
✅ 가능
✅ 가능
주 용도
인증 / 보안 / 인코딩
권한 / 로깅
비즈니스

1) LoginFilter.java

C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\filter\LoginFilter.java
package com.example.boardv1._core.filter; import java.io.IOException; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("----------------login filter----------------"); } }

2) FirstFilter.java

C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\filter\FirstFilter.java
package com.example.boardv1._core.filter; import java.io.IOException; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; public class FirstFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("----------------first filter----------------"); } }

3) FilterConfig.java

① 코드

C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\filter\FilterConfig.java
package com.example.boardv1._core.filter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<FirstFilter> firstFilter() { FilterRegistrationBean<FirstFilter> bean = new FilterRegistrationBean<>(); bean.setFilter(new FirstFilter()); bean.addUrlPatterns("/*"); // 모든 요청에 대해 검사 bean.setOrder(1); // 필터 순서 return bean; } @Bean public FilterRegistrationBean<LoginFilter> loginFilter() { FilterRegistrationBean<LoginFilter> bean = new FilterRegistrationBean<>(); bean.setFilter(new LoginFilter()); bean.addUrlPatterns("/*"); // 모든 요청에 대해 검사 bean.setOrder(2); // 필터 순서 return bean; } }

② 테스트

notion image
notion image

4) FirstFilter.java - 브라우저에 출력

① 코드

package com.example.boardv1._core.filter; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; public class FirstFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("----------------first filter----------------"); PrintWriter pw = response.getWriter(); pw.println("block"); } }

② 테스트

notion image

5) FirstFilter.java - doFilter()

① 코드

package com.example.boardv1._core.filter; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; public class FirstFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("----------------first filter----------------"); // PrintWriter pw = response.getWriter(); // pw.println("block"); chain.doFilter(request, response); } }

② 테스트

notion image

6) LoginFilter.java

package com.example.boardv1._core.filter; import java.io.IOException; import com.example.boardv1.user.User; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("------Login Filter-----------"); HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; HttpSession session = req.getSession(); User sessionUser = (User) session.getAttribute("sessionUser"); // 예외: GET /boards/{id} 상세보기는 인증 없이 허용 String uri = req.getRequestURI(); if ("GET".equals(req.getMethod()) && uri.matches(".*/boards/\\d+$")) { chain.doFilter(request, response); return; } // /boards/*, /replies/* 나머지 전부 인증 필요 if (sessionUser == null) { resp.sendRedirect("/login-form"); return; } chain.doFilter(request, response); } }

7) FilterConfig.java

① 코드

package com.example.boardv1._core.filter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<FirstFilter> firstFilter() { FilterRegistrationBean<FirstFilter> bean = new FilterRegistrationBean<>(); bean.setFilter(new FirstFilter()); bean.addUrlPatterns("/*"); // 모든 요청에 대해 검사 bean.setOrder(1); // 필터 순서 return bean; } @Bean public FilterRegistrationBean<LoginFilter> loginFilter() { FilterRegistrationBean<LoginFilter> bean = new FilterRegistrationBean<>(); bean.setFilter(new LoginFilter()); bean.addUrlPatterns("/boards/*"); bean.addUrlPatterns("/replies/*"); bean.setOrder(2); // 필터 순서 return bean; } }

② 테스트

로그인하지 않고 update-form에 접근했을 때
로그인하지 않고 update-form에 접근했을 때

2. 인증 (인터셉트)

1) LoginInterceptor.java

C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\interceptor\LoginInterceptor.java
package com.example.boardv1._core.interceptor; import org.jspecify.annotations.Nullable; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.example.boardv1._core.errors.ex.Exception401; import com.example.boardv1.user.User; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; public class LoginInterceptor implements HandlerInterceptor { // 뷰가 완성되면 실행 -> 쓸 일이 거의 없음 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { System.out.println("------------------view render complete------------------"); } // 컨트롤러 메서드 호출 직전에 실행 (/boards, /replies) @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); // localhost:8080/hello/5 -> /hello/5 // 예외: /boards/숫자 패턴 -> 인증 체크 안 함 if (uri.matches(".*/boards/\\d+$")) { return true; } HttpSession session = request.getSession(); User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { throw new Exception401("인증되지 않았습니다"); } return true; // 로그인 -> true -> 메서드 진입 / 로그인 X -> false -> 진입불가 } // 컨트롤러 메서드 호출 직후에 실행 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("------------------postHandle complete------------------"); } }

2) WebMvcConfig.java

C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\interceptor\WebMvcConfig.java
package com.example.boardv1._core.interceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/boards/**") .addPathPatterns("/replies/**") .excludePathPatterns("/boards/[0-9]+"); } }
 
Share article