Back-End/Spring

[Spring MVC2][로그인 처리] - 서블릿 필터(공통의 관심사 해결 방안)

얄루몬 2022. 5. 19. 18:50

💻본 포스팅은 '스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 김영한'님의 강의를 듣고 작성되었습니다.

https://inf.run/vQHp

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com


목차
1. 공통의 관심 사항
    - 서블릿 필터
    - 스프링 인터셉터
2. 서블릿 필터
3. 서블릿 필터 정리

공통의 관심 사항

    • 로그인 한 사용자만 상품 관리 페이지에 들어갈 수 있어야 한다는 요구사항이 있을 경우 개발자는 로그인 하지 않은 권한 없는 사람이 접근 제한한 부분을 들어가는 것을 기술적으로 막아두어야 한다.
    • 이때 로그인 하지 않을 때 꽤나 많은 접근의 제한이 있어야 하고 우리는 이를 위해 로그인 여부 체크하는 로직을 하나 넣어주면 되지만 매우 많은 컨트롤러에 공통으로 로그인 여부를 체크해주어야 하기에 상황이 복잡해진다.
    • 이처럼 공통으로 인증(로그인 했는지 안 했는지)에 관심을 가지고 있는 경우를 공통 관심사라고 한다.
    • 스프링의 AOP로 공통 관심사 문제를 해결할 수 있지만 웹과 관련된 공통 관심사의 경우엔 서블릿 필터, 혹은 스프링이 제공하는 인터셉터를 사용하는 것이 바람직하다.
    • 웹과 관련된 공통 관심사 처리는 HTTP 헤더나 URL 정보들이 필요하고 서블릿 필터와 스프링의 인터셉터는 HttpServletRequest를 제공해 이 문제를 처리할 수 있게 한다.

서블릿 필터

필터는 서블릿이 지원하는 수문장이라고 생각하면 편하다.

필터의 특성

필터 흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러

 

필터 제한
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터(적절하지 않은 요청이라 판단, 서블릿 호출X) //비로그인 사용자

필터에서 적절하지 않은 요청이라고 판단하면 서블릿까지 가지 않게 해서 끝을 내준다. (이때 로그인 여부를 확인하기에 좋다.)

 

필터 체인
HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러

필터는 체인으로 구성되어 있고 중간에 필터를 자유롭게 추가할 수 있다.

서블릿 필터 - 요청로그

모든 요청을 로그로 남기는 필터

필터의 경우엔 FilterRegistrationBean를 사용해서 Bean등록을 해주어야 사용할 수 있다.

서블릿 필터 - 인증 체크

로그인 하지 않은 사용자는 상품 관리뿐만 아닌 미래에 개발되고 수정될 페이지에 접근할 수 없는 경우에도 접근할 수 없게 하자

인증 체크 필터

package hello.login.web.filter;


import hello.login.web.session.SessionConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.PatternMatchUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@Slf4j
public class LoginCheckFilter implements Filter{

    private static final String[] whiteList = {"/", "/members/add", "/login","/logout","/css/*"};
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();

        HttpServletResponse httpResponse = (HttpServletResponse) response;

        try{
            log.info("인증 체크 필터 시작{}", requestURI);
            if(isLoginCheckPath(requestURI)){
                log.info("인증 체크 로직 실행 {}",requestURI);
                HttpSession session = httpRequest.getSession(false);
                if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER)==null){

                    log.info("미인증 사용자 요청{}", requestURI);
                    httpResponse.sendRedirect("/login?redirectURL="+requestURI);
                    return; //여기서가 중요 인증하지 않은 사용자라면 요청을 끝내야 됨(서블릿, 컨트롤러 호출 X)
                }

            }
            chain.doFilter(request,response);
        } catch (Exception e){
            throw e;//예외 로깅 가능하지만 톰캣까지 예외를 보내주어야 한다.
        } finally {
            log.info("인증 체크 필터 종료{}", requestURI);
        }
    }

    //화이트 리스트의 경우 인증 체크 X
    private boolean isLoginCheckPath(String requestURI){
        return !PatternMatchUtils.simpleMatch(whiteList, requestURI);
    }
}
  • whiteList에는 로그인 없이도 접근 가능한 페이지를 허용할 수 있게 담아주어야 한다.
    • 회원가입이나 로그인 화면과 같은 부분은 로그인 하지 않은 사용자도 접근할 수 있어야 하기때문이다.
  • 인증을 하지 않은 사용자라면 다음 단계로 나가지 않게 끝내주어야 한다.(컨트롤러를 호출하게 되면 인증 없이도 접근 가능해지게 된다.)

 

필터 등록

package hello.login;

import hello.login.web.filter.Logfilter;
import hello.login.web.filter.LoginCheckFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean logFilter(){
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new Logfilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean logCheckFilter(){
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LoginCheckFilter());
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;
    }


}

 

서블릿 필터 정리

  • 서블릿 필터를 잘 사용하면 로그인 하지 않은 사용자가 허용하지 않은 경로로 가는 것을 막을 수 있다.
  • 공통의 관심사를 서블릿 필터를 사용해서 해결한 덕분에 향후 로그인 관련 정책의 변경이 있더라도 이 부분만 변경하면 되게 된다.