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 파일을 만들어 인터셉터를 등록한다.
이 과정까지 끝내야 인터셉터가 정상적으로 작동한다.
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"
일 때는) 인터셉터가 적용되지 않는다.
참고 사이트
잘 읽었습니다. 정리 잘해주셔서 도움이 많이 됐네요!!