[Spring Boot + JPA with Kotlin] Interceptor로 Annotation이 있는지 확인하기

Minji Kwak·2022년 10월 20일
0
post-thumbnail

JWT 인증을 구현할 때 인증이 필요한 메소드마다 인증 코드를 집어 넣는 대신 커스텀 어노테이션을 붙인 URI에 한해 인터셉터가 토큰을 체크하는 방식을 사용하고자 한다.

커스텀 어노테이션 생성

Authenticated라는 어노테이션을 만들어서 URI에 사용할 것이다.
Kotlin의 경우, 커스텀 어노테이션을 만들기 위해서는 class 앞에 annotation만 붙이면 된다.
Target, Retention을 추가적으로 설정할 수 있지만 이 코드에서는 중요한 부분이 아니므로 넘어간다.

annotation class Authenticated

인터셉터 구현

인터셉터를 구현하기 위해 HandlerInterceptor를 상속받은 AuthInterceptor를 만든다.
인터셉터는 preHandle, postHandle, afterCompletion이라는 3개의 메소드를 갖고 있다.
각각 컨트롤러의 동작 전, 컨트롤러 동작 이후,view 렌더링 이후 시행되는 메소드이다.
JWT 인증을 위해서는 컨트롤러가 동작하기 전 토큰의 유효성을 판단해야 하기 때문에 preHandle을 사용한다.

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerInterceptor
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

@Component
class AuthInterceptor: HandlerInterceptor {
    @Autowired
    private lateinit var authTokenService: AuthTokenService // 토큰의 유효성을 확인할 함수를 불러오는 곳.

    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { 
        if (handler is HandlerMethod) {
        	// Authenticated 어노테이션이 있는지 검사
            var isAuthenticated = handler.getMethodAnnotation(Authenticated::class.java) 
            if (isAuthenticated != null) {
            	// "Authorization" 헤더의 value 가져오기
                val authToken = request.getHeader("Authorization") 
                // "Authorization" 헤더가 없을 때
                if (authToken == null) {
                    throw Seminar400("토큰 값을 넣어주세요.")
                // "Authorization" 헤더가 있을 때
                } else {
                    authTokenService.verifyToken(authToken) // 토큰의 유효성 확인
                }
            }
            return true
        } else {
            return false
        }
    }
}

+ 추가 설명
if (handler is HandlerMethod)는 요청에서 요구한 handler가 controller에 있는 메소드인지 확인하는 부분이다. HandlerMethod는@RequestMapping과 하위 어노테이션이 붙은 메소드를 추상화한 객체로, handler가 HandlerMethod가 아닐 경우, controller에 없는 메소드라는 뜻이기 때문에 넘어가는 것이다.

WebConfig 추가

webconfig 파일을 만들어 인터셉터를 등록한다.
이 과정까지 끝내야 인터셉터가 정상적으로 작동한다.

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
class WebConfig(
    private val authInterceptor: AuthInterceptor
) : WebMvcConfigurer {

    override fun addInterceptors(registry: InterceptorRegistry) {
        registry.addInterceptor(authInterceptor)
        .addPathPatterns("/api/v1/**") //"/api/v1/"이 붙은 모든 URI에 대해 인터셉터를 적용시킨다는 의미
    }
}

참고로 .addPathPatterns("/api/v1/**")에서 *을 2개 붙여야 "/api/v1/"의 하위에 있는 모든 uri에 대해 인터셉터를 적용할 수 있다.
한 개만 붙일 경우, "/api/v1/"에서 한개의 경로를 더한 uri(예를 들면, "/api/v1/user/")까지만 인식을 할 수 있고 경로가 둘 이상 추가되면 (예를 들어 "/api/v1/user/me"일 때는) 인터셉터가 적용되지 않는다.

참고 사이트

profile
코딩 걸음마 떼는 중

1개의 댓글

comment-user-thumbnail
2023년 10월 23일

잘 읽었습니다. 정리 잘해주셔서 도움이 많이 됐네요!!

  • .addPathPatterns이 없으면 모든 path에 대해서 interceptor가 적용되네요.
답글 달기