@Component
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws UnauthorizedException {
try {
if (isNeedToAuth((HandlerMethod)handler)) {
String userIdBySession = getUserIdBySession(request);
String userIdByPath = getUserIdByPathVariable(request);
if (!userIdBySession.equals(userIdByPath)) {
throw new UnauthorizedException();
};
}
return true;
} catch (Exception e) {
throw new UnauthorizedException(e);
}
}
// 기타 메소드 생략
}
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(authInterceptor);
}
}
대략 @AuthRequired
어노테이션이 붙은 MethodHandler 에 로그인 세션 검증을 추가하는 인터셉터를 만들었다!
그런데 문제는 일반적으로 404 에러가 발생해야 하는 "서비스하지 않는 URL"을 요청했을 때,
ResourceHttpRequestHandler
를 HandlerMethod
로 타입변환 할 수 없다는 에러가 발생한다.
별다른 설정이 없는 한, 서버 에러를 띄우게 된다!
java.lang.ClassCastException:
class org.springframework.web.servlet.resource.ResourceHttpRequestHandler cannot be cast to class org.springframework.web.method.HandlerMethod (org.springframework.web.servlet.resource.ResourceHttpRequestHandler and org.springframework.web.method.HandlerMethod are in unnamed module of loader 'app')
스프링 2.X 프레임워크에서 정적 리소스를 요청할 때에도 인터셉터가 호출되기 때문에,
인터셉터에 전달되는 핸들러가 모두 HandlerMethod가 아닐 수 있다는 것이다!
API 로 등록되지 않은 모든 URL을 정적 리소스를 요청하는 URL 로 바꾸는 것은 성능 향상을 위해 2.X 부터 바꾼 것으로 추측해본다.
정적 리소스
- 클라이언트로부터 요청이 들어왔을 때, 요청에 대한 리소스가 이미 만들어져 있어 그대로 응답하는 경우
- html, css, js, image 등
HandlerMethod
로 들어올 것이라고 예상했으나, 실제로 받았던 이 핸들러가 바로 '정적 리소스를 처리하는 핸들러' 이다!200
응답 코드에, Last-modified
헤더를 함께 보낸다.If-Modified-Since
헤더를 포함해서 보낸다.If-Modified-Since
헤더값과 리소스의 변경 시점이 같다면304
상태코드를 응답하면서 다시 리소스를 응답하지 않는다.If-Modified-Since
헤더값과 리소스의 변경 시점이 다르다면200
상태코드를 응답하게 되면서 브라우저는 다시 응답의 Last-Modified
헤더값을 다음 요청의 If-Modified-Since
헤더값에 포함하여 전송한다.@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(authInterceptor)
.addPathPatterns("/rest-api-root/**");;
}
}
이렇게 addPathPatterns
를 이용해서, REST API 의 루트 URL 을 명시해주면, 정적 요청없이 잘 동작한다!