241210 Handler Interceptor 로 인증인가 하기

물고기가자라면어그로·2024년 12월 10일
0

저번 뉴스피드 프로젝트는 Filter로 인증인가를 진행했다.
그런데 이번에는 특강 때 Handler Interceptor에 대해 배워서 이를 써먹어보기로 했다.

Handler Interceptor

Handler Interceptor 란?

: Spring Context에서 특정 URI 호출을 가로채어 Controller에 닿기 전 후에 제어를 할 수 있게끔 하는 interceptor

필터가 spring 컨텍스트 안에 들어가기 전에 한번 걸러주는 역할이라면
handler interceptor는 request가 필터를 지나 컨트롤러에 도달하기 전이나 후에 걸러주는 역할인 것이다.

Filter vs Handler Interceptor

FilterHandler Interceptor
호출 시점DispatcherServlet 처리 전에 호출DispatcherServlet 실행 후에 호출
실행 영역동일한 웹 애플리케이션의 영역 내에서 동작하므로, 스프링의 Context에 접근하기 어려움스프링에서 관리되기 때문에 스프링 컨텍스트 내의 모든 객체(빈)에 접근이 가능
용도보안 관련 공통 작업, 모든 요청에 대한 로깅 또는 감사, 이미지/데이터 압축 및 문자열 인코딩 등인증/인가 등과 같은 공통 작업, Controller로 넘겨주는 정보의 가공, API 호출에 대한 로깅 또는 감사 등

이번 프로젝트에서는 앞서 말했다시피 새롭게 배운 기능을 사용해 보고 싶기도 했고
예외처리를 해줄 수 있다는 것이 큰 이점 같아 handler Interceptor로 인증인가를 처리해보기로 했다.

Handler Interceptor 적용하기

Handler Interceptor를 적용시키기 위해서는 두 가지 절차가 필요하다.

  1. Handler Interceptor 만들기
  2. WebConfig에서 InterceptorRegistry에 Handler Interceptor 등록하기

Handler Interceptor 만들기

Handler Interceptor는 인터페이스이며 언제 호출되냐에 따라서 세 가지 메서드를 사용할 수 있다.

  • preHandle(): Controller 처리 이전에 수행.
  • postHandle(): Controller 처리 이후 수행.
  • afterCompletion(): request 처리가 완료되고 뷰 렌더링 이후 수행.

나는 이번 프로젝트에서 로그인 여부 확인해줄 Handler Interceptor와 접근 권한을 확인할 Handler Interceptor 들을 만들기로 했기 때문에

예를 들어 가게를 생성하는 기능은 우선 로그인이 되어있어야할 것이고, 사장님(OWNER)의 권한을 가지고 있어야할 것이다. 그럼 LoginInterceptorOwnerRoleInterceptor의 두 가지 handler interceptor를 거쳐 storeController에 가면 될 것이다.

LoginInterceptor

@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ResponseStatusException {

        HttpSession session = request.getSession(false);

        if (session == null) {
            throw new WrongAccessException(ExceptionType.NOT_LOGIN);
        }

        if (session.getAttribute(Const.LOGIN_USER) == null) {
            throw new WrongAccessException(ExceptionType.NOT_LOGIN);
        }

        return true;
    }
}

HandlerInterceptorimplents 한 뒤 preHandle 메서드를 @Override 해서 실행할 동작을 작성한다.

이 때, true를 반환하면 체인에 의해 다음 순서의 Interceptor를 실행시킨다.

*참고로 이 코드 예시는 소셜로그인을 적용시키기 전 작성한 Interceptor이다.

OwnerInterceptor

@Component
public class OwnerRoleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ResponseStatusException {

        HttpSession session = request.getSession(false);

        if (session == null) {
            throw new WrongAccessException(ExceptionType.CUT_OF_SESSION);
        }

        User loginUser = (User) session.getAttribute(Const.LOGIN_USER);

        if (loginUser.getRole() != Role.OWNER) {
            throw new WrongAccessException(ExceptionType.NEED_AUTHORITY_OWNER);
        }

        return true;
    }
}

마찬가지로 preHandle@Override하여 실행 동작을 적어준 뒤 true를 반환시켜 다음으로 넘긴다.

Handler Interceptor 등록하기

그렇다면 이제 WebConfig에서 Interceptor를 등록해야한다.
방법은 Filter 때와 유사했는데 FilterRegistrationBean에 저장하는 것이 아니라 InterceptorRegistry에 저장한다.

WebMvcConfigurer가 가진 addInterceptors라는 메서드를 @Override 하여 만든 Handler Interceptor와 그를 적용시킬 URI 패턴들을 등록해준다.

.addInterceptors(인터셉터명) : 레지스트리에 인터셉터 등록
.addPathPatterns(uri 패턴) : 인터셉터를 등록할 uri 패턴 설정
.excludePathPatterns(uri 패턴) : 인터셉터를 건너뛸 uri 패턴 설정

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private static final String[] LOGIN_REQUIRED_PATH_PATTERNS = {"/api/**"};
    private static final String[] LOGIN_EXCLUDE_PATH_PATTERNS = {"/api/users/signup", "/api/users/login"};
    private static final String[] OWNER_ROLE_REQUIRED_PATH_PATTERNS = {"/api/owners/**"};

    private final LoginInterceptor loginInterceptor;
    private final OwnerRoleInterceptor ownerRoleInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns(LOGIN_REQUIRED_PATH_PATTERNS)
                .excludePathPatterns(LOGIN_EXCLUDE_PATH_PATTERNS)
                .order(Ordered.HIGHEST_PRECEDENCE);

        registry.addInterceptor(ownerRoleInterceptor)
                .addPathPatterns(OWNER_ROLE_REQUIRED_PATH_PATTERNS)
                .order(Ordered.HIGHEST_PRECEDENCE + 2);
    }

0개의 댓글

관련 채용 정보