Spring Filter로 세션 페이징하기

dogineer·2023년 8월 21일
0
post-thumbnail

토이 프로젝트에서 세션 로그인 인증을 통해 외부인이 접근할 경우 아래와 같은 오류 화면이 표시되는 서비스가 있습니다.
이 서비스 기능을 페이지 컨트롤러마다 코드를 추가하는 대신, 이를 한 곳에서 처리하기 위해 Filter를 도입해보았습니다.

밑의 코드는 세션에 있는 'account'와 'access'를 검증하여 커스텀 예외를 발생시키는 코드입니다.

public class AuthChecker {
    public void blockOutsiders(HttpSession session) throws CustomException {
        if (session.getAttribute("account") == null){
            throw new CustomException(AuthErrorCode.ACCOUNT_NOT_FOUND);
        } else if (session.getAttribute("access").hashCode() == 0) {
            throw new CustomException(AuthErrorCode.AUTH_ACCESS_NOT_FOUND);
            }
    }
}

필터 클래스 만들기

필터 (Filter) 서블릿으로 들어오는 요청과 서블릿에서 나가는 응답을 가로채서 조작하거나 처리하는 역할을 합니다.

세션으로 사용자 인증을 통해 예외를 발생시키는 필터를 만들어보겠습니다.

'PageRequestAuthFilter' 클래스를 생성하여 'OncePerRequestFilter'를 상속받아 줍니다.
그 다음 'doFilterInternal' 메서드에 위에 적어준 (authChecker.blockOutsiders(session)) 코드를 참고하여 아래와 같이 작성하였습니다.

public class PageRequestAuthFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpSession session = request.getSession(false);

        log.info("[R] 사용자 인증 필터 실행");
        
        if (session == null || session.getAttribute("account") == null) {
            log.error("[!] 사용자 인증 실패");
            throw new CustomException(AuthErrorCode.ACCOUNT_NOT_FOUND);
        } else if (session.getAttribute("access").hashCode() == 0) {
            log.error("[!] 사용자 승인 인증 실패");
            throw new CustomException(AuthErrorCode.AUTH_ACCESS_NOT_FOUND);
        }

        log.info("[S] 사용자 인증 성공");
        filterChain.doFilter(request, response);
    }

필터를 사용하기 위해 'WebConfig' 클래스에 'Bean'을 등록하는 코드를 작성합니다.

@Bean
    public FilterRegistrationBean<PageRequestAuthFilter> pageRequestAuthFilterRegistration() {
        FilterRegistrationBean<PageRequestAuthFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new PageRequestAuthFilter()); // 필터 등록
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 필터 순서
        registrationBean.addUrlPatterns("/page/user/*"); // URL 패턴
        return registrationBean;
    }

세션 인증으로 예외를 발생시키는 필터가 구축되었습니다.

그러나 예외가 발생하더라도 @ControllerAdvice 어노테이션이 작동하지 않는 상황이 있을 수 있습니다.

Filter에서 @ControllerAdvice 예외 처리가 작동하지 않아요

@ControllerAdvice란? 모든 @Controller 전역에서 발생할 수 있는 예외를 잡아 처리해주는 annotation

위에서 예외가 발생하지만 @ControllerAdvice 어노테이션이 작동하지 않는 상황을 마주했습니다.

@ControllerAdvice는 컨트롤러 계층에서 발생한 예외에 대한 처리를 담당하기 때문입니다.
필터는 컨트롤러에 이르기 전의 단계에서 동작하므로, 필터에서 발생한 예외에 대해서는 @ControllerAdvice의 예외 처리가 동작하지 않습니다.

요청 -> 서블릿 -> 필터 -> 컨트롤러 -> 클라이언트 응답

따라서 필터에서 예외 처리를 하기 위해서는 해당 필터 내에서 예외 처리 로직을 직접 구현해보겠습니다.

'PageRequestAuthFilter' 클래스에서 'doFilterHandleException' 메소드를 작성합니다.

private void doFilterHandleException(CustomException e, HttpServletResponse response) throws IOException {
        log.error("[!] Error Code : " + e.getErrorCode());
        log.error("[!] Error Message : " + e.getMessage());

        String redirectUrl = "/error/view?error=" + e.getErrorCode().name()
            + "&code=" + e.getErrorCode().code()
            + "&message=" + URLEncoder.encode(e.getErrorCode().getMessage(), StandardCharsets.UTF_8);

        response.sendRedirect(redirectUrl);
    }

마지막으로 try catch를 통해 예외 처리를 진행하였습니다.

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpSession session = request.getSession(false);

        log.info("[R] 사용자 인증 필터 실행");

        try {
            if (session == null || session.getAttribute("account") == null) {
                log.error("[!] 사용자 인증 실패");
                throw new CustomException(AuthErrorCode.ACCOUNT_NOT_FOUND);
            } else if (session.getAttribute("access").hashCode() == 0) {
                log.error("[!] 사용자 승인 인증 실패");
                throw new CustomException(AuthErrorCode.AUTH_ACCESS_NOT_FOUND);
            }

            log.info("[S] 사용자 인증 성공");
            filterChain.doFilter(request, response);
        } catch (CustomException e) {
            doFilterHandleException(e, response);
        }
    }

그럼 사용자 인증에 따른 오류 페이지가 정상적으로 나오는걸 확인할수있습니다.

  • log

  • ACCOUNT_NOT_FOUND("A0001", "등록된 계정이 없거나 비정상적인 접근입니다."),

  • AUTH_ACCESS_NOT_FOUND("A0002", "승인이 필요합니다. 관리자에게 문의하세요.")
profile
왈왈왈 메모장입니다 왈왈왈

0개의 댓글