[스프링부트] 익명 블로그 V5 - 인증 블로그 V1 - 필터(Filter)로 인가(권한 부여)

익명 블로그 V5(인증 블로그 V1) - 3
이나겸's avatar
Nov 29, 2024
[스프링부트] 익명 블로그 V5 - 인증 블로그 V1 - 필터(Filter)로 인가(권한 부여)
💡

필터(Filter)

HTTP 요청과 응답을 가로채서 필요한 로직 실행, 처리
주로 웹 애플리케이션의 특정 기능 구현을 위해 사용
인증, 인가, 로깅, 요청 유효성 검사 등의 작업 수행에 유용
 

개념

  • 요청 및 응답 가로채기
    • 클라이언트(브라우저)에서 서버로 들어오는 요청이나 서버에서 클라이언트로 나가는 응답 가로채서 필요한 로직 실행, 처리
  • 재사용 가능한 컴포넌트
    • 여러 서블릿이나 JSP에 대해 공통적으로 수행해야 하는 작업을 하나의 필터로 구현하여 재사용
  • 체인(chain) 구조
    • 여러 필터를 체인 구조로 연결하여 순차 실행 가능
    • 각 필터는 다음 필터를 호출하거나 체인 중단 할 수 있음
 

필터의 라이프 사이클

  1. init()
      • 필터가 초기화될 때 한 번 호출되고 초기화 작업 수행 가능
  1. doFilter()
      • 클라이언트의 요청을 가로채고 필요한 작업을 수행한 후 다음 필터 또는 서블릿 호출 ⇒ 필터의 핵심 메서드
  1. destroy()
      • 필터가 제거될 때 호출되고 리소스 해제하는 등의 정리 작업 수행 가능
 

인증 블로그에 필터 활용하기

필터를 활용하지 않고 인가(권한 부여) 하는 경우

  • session에 sessionUser라는 값이 있는지 확인 ⇒ 로그인(인증) 체크
  • 값이 없으면(로그인을 안 한 상태) 로그인 페이지로 강제 이동
  • 글 쓰기, 수정, 삭제, 댓글 쓰기 등은 로그인이 되어있는 상태에서만 가능해야하기 때문에 코드 내용을 필요한 컨트롤러 내부의 메서드마다 작성해야하는 불편함 발생
User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { resp.sendRedirect(req.getContextPath() + "/login-form"); }
  • BoardController 클래스에서의 예
    • 로그인 안 된 상황에서는 글쓰기 페이지 진입을 막는 코드 작성
    • 수정, 삭제 등의 경우가 추가되면 각 메서드들마다 이렇게 코드를 작성해야하는 불편함 발생
@RequiredArgsConstructor @Controller public class BoardController { private final HttpSession session; @GetMapping("/") public String index() { return "index"; } @GetMapping("/board/save-form") public String saveForm() { // 이렇게 작성하지않으면 로그인(인증)되지않은 상태에서 글쓰기가 가능하게 됨 // 글 다 적고 글쓰기 버튼 눌렀는데 로그인하라고 로그인 페이지로 넘어가버림 // 로그인 하고 왔는데 글 적어놓은거 날아가서 다시 적어야됨 // 그래서 로그인(인증) 안했으면 글쓰기 페이지 진입 못하도록 막아놓기 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { throw new RuntimeException("인증되지 않음 401"); } return "board/save-form"; } }
 

필터 활용

[AuthenticationFilter 클래스 - 필터 클래스]

  • Filter 인터페이스 구현
    • 이 필터는 클라이언트의 요청을 가로채고, 요청 처리 전에 인증 검사
    • Filter import 할때는 jakarta로 해야함
  • doFilter 메서드 오버라이드
    • 필터의 핵심 메서드
    • 요청을 가로채고 처리 수행 후 다음 필터 또는 서블릿으로 전달
  • servletRequest, servletResponse 객체를 HttpServletRequest, HttpServletResponse로 다운캐스팅
    • HTTP 요청과 응답에 대한 구체적인 기능 사용 가능
  • HttpSession session = req.getSession()
    • HttpServletRequest 객체를 통해 세션 가져옴
    • 세션은 사용자와 서버 간의 상태 정보를 저장하는 공간
  • User sessionUser = (User) session.getAttribute("sessionUser")
    • 세션에서 로그인된 사용자 정보를 담은 sessionUser를 가져옴
  • 인증 여부 검사
    • sessionUser가 null이면 사용자가 로그인되지 않은 상태이므로 로그인 페이지(/login-form)로 리다이렉트
  • 인증된 사용자 처리
    • seeeionUser가 null이 아니면 사용자가 인증된 상태이므로 요청을 다음 필터 또는 서블릿으로 전달
package com.metacoding.authblog._core.filter; import com.metacoding.authblog.user.User; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import java.io.IOException; public class AuthenticationFilter implements Filter { // FilterChain : 다음 필터 호출하기 위한 매개변수 // doFilter 메서드 오버라이드 : 요청을 가로채고 처리 수행 후 다음 필터 또는 서블릿으로 전달 @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("돌고 있나?"); // 다운캐스팅 HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; // 리퀘스트 객체에서 세션 가져오기 HttpSession session = req.getSession(); // 세션에서 로그인된 사용자 정보를 담은 sessionUser를 가져옴 User sessionUser = (User) session.getAttribute("sessionUser"); // 인증 여부 검사 if (sessionUser == null) { resp.sendRedirect("/login-form"); // 로그인 페이지로 리다이렉트 } else { // 인증된 사용자 처리 filterChain.doFilter(servletRequest, servletResponse); } } }
 

[FilterConfig 클래스 - 설정 파일 생성]

  • 인증 필터인 AuthenticationFilter를 특정 URL 패턴에 적용하도록 구성
  • @Configuration
    • Spring Framework에서 필터를 설정하고 등록하는 구성 클래스
    • Spring IoC 컨테이너는 이 클래스의 빈(bean) 정의를 처리
    • @Component와 @Configuration의 차이
      • @Component : 간단하게 필터 등록, 자동 감지, 기본적으로 모든 요청에 대해 적용
      • @Configuration : URL 패턴, 필터 순서 등 더 많은 제어와 세부 설정 가능
  • @Bean
    • 이 메서드가 Spring 컨테이너에 의해 관리되는 빈을 생성함을 나타냄
    • addAuthFilter() 메서드는 FilterRegistrationBean을 반환하고, 반환값은 IoC 컨테이너에 등록됨
  • FilterRegistrationBean rg = new FilterRegistrationBean() rg.setFilter(new AuthenticationFilter())
    • FilterRegistrationBean 객체를 생성하고, AuthenticationFilter를 필터로 설정
    • AuthenticationFilter는 사용자 인증을 처리하는 필터
  • 필터가 적용될 URL 패턴 등록
    • 주소가 /board 이거나 /user로 시작하는 모든(*) 경우에만 적용
    • 이 경로에 대한 요청은 모두 AuthenticationFilter를 거침
  • rg.setOrder(1)
    • 필터 실행 순서 설정
    • 숫자가 작을수록 우선 순위가 높아지고 1은 첫번째로 실행됨 의미
  • return rg;
    • 설정이 완료된 FilterRegistrationBean 반환하여 IoC 컨테이너에 등록
package com.metacoding.authblog._core.config; import com.metacoding.authblog._core.filter.AuthenticationFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; // 설정 파일에는 @Configuration, 그 외 용도의 파일에는 @Component @Configuration // @Component 포함 public class FilterConfig { @Bean // 리턴값이 IOC에 등록됨, @Bean이 붙어있는 메소드 찾아내서 필터 장착시킴 public FilterRegistrationBean addAuthFilter() { // FilterRegistrationBean 객체를 생성하고, AuthenticationFilter를 필터로 설정 // AuthenticationFilter는 사용자 인증을 처리하는 필터 FilterRegistrationBean rg = new FilterRegistrationBean(); rg.setFilter(new AuthenticationFilter()); // URL 패턴 등록 // 주소가 /board 이거나 /user로 시작하는 경우에만 (*은 모든) rg.addUrlPatterns("/board/*"); rg.addUrlPatterns("/user/*"); rg.setOrder(1); // 필터 실행 순서 설정 // addAuthFilter() 메서드는 FilterRegistrationBean을 반환하고, // 반환값은 IoC 컨테이너에 등록됨 return rg; } }
Share article

Nakyeom's Study