Spring

Interceptor

테니드2 2025. 4. 15. 13:30

Filter와 매우 유사한 형태로 존재 하지만, 차이점은 Spring Context에 등록 된다.

→ 핸들러, 컨트롤러, 메서드 정보 접근 가능

 

AOP와 유사한 기능을 제공 할 수 있으며,

주로 인증 단계를 처리 하거나, Logging 하는 데에 사용

이를 선/후 처리 함으로써, Service business logic과 분리 시킴

 

package com.example.demo.annotation;

import java.lang.annotation.*;
import java.lang.reflect.Method;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Auth {
}

@Auth 검증이 필요한 빈,메소드 등에 사용할 어노테이션을 만든다

 

 

package com.example.demo.controller;


import com.example.demo.annotation.Auth;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/private")
@Auth
public class PrivateController {


    @GetMapping("/hello")
    public String hello(){
        return "private hello";
    }
}

인터셉터단에서 검증이 필요한 빈이나 메소드등에 @Auth 어노테이션을 추가한다,

 

 

package com.example.demo.interceptor;

import com.example.demo.annotation.Auth;
import com.example.demo.exception.AuthException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;


@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String url = request.getRequestURI();

        URI uri = UriComponentsBuilder.fromUriString(url)
                .query(request.getQueryString())
                .build()
                .toUri();


        log.info("request url : {}",url);
        boolean hasAnnotation = checkAnnotation(handler, Auth.class);
        log.info("has annotation : {}",hasAnnotation);

        // Auth 권한을 가진 요청에 대해서는 확인 ex) 세션, 쿠키
        if(hasAnnotation){
            // 권한체크
            String query = uri.getQuery();
            log.info("query : {}", query);
            if(query.equals("name=jun")){
                return true;
            }

            throw new AuthException();

        }

        return true;
    }

    private  boolean checkAnnotation(Object handler,Class clazz){

        // resource javascript, html, etc
        if (handler instanceof ResourceHttpRequestHandler){
            return true;
        }


        // annotation
        HandlerMethod handlerMethod = (HandlerMethod)handler;


        if(null != handlerMethod.getMethodAnnotation(clazz) || null != handlerMethod.getBeanType().getAnnotation(clazz)){
            // Auth annotation 있을때 true
            return true;
        }

        return false;

    }
}

handler에서 @Auth 어노테이션이 붙는 요청을 확인하여 특정 로직(인증)를 수행하는 예제이다.

 

 

package com.example.demo.config;

import com.example.demo.interceptor.AuthInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class MvcConfig implements WebMvcConfigurer {

    private final AuthInterceptor authInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor).addPathPatterns("/api/private/*");
    }
}

 

Configuration에서 WebMvcConfigurer 인터페이스를 구현하고 addInterceptors 메소드에서 registry.addInterceptor 함수 인자에 추가하려는 인터셉터를 추가한다.

addPathPatterns 함수를 이용하여 특정 URI 패턴에만 인터셉터를 적용할 수 있다.