개념
Filter와 매우 유사한 형태로 존재
차이점은 Spring Context에 등록된다는 것
Spring Context에 등록되기 때문에 스프링에 대한 기능 활용 가능
AOP와 유사한 기능 제공 가능
사용
Filter : 순수한 log 남기기
Interceptor : 인증
contoller 패키지
PrivateController 클래스 : 아무 권한이 없는 사용자 모두가 들어올 수 있도록 함
PublicController 클래스 : 아무나 접근할 수 없음, 세션이 인증된 사용자만 접근 가능
annotation 패키지
interceptor 패키지
config 패키지
exception 패키지
handler 패키지
package com.example.intercepter.controller;
import com.example.intercepter.annotation.Auth;
import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class PrivateController {
@GetMapping("/hello")
public String hello() {
log.info("private hello controller");
return "private hello";
}
}
package com.example.intercepter.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/public")
public class PublicController {
@GetMapping("/hello")
public String hello() {
return "public hello";
}
}
package com.example.intercepter.annotation;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Auth {
}
implements HandlerInterceptor
@Slf4j : log 찍기 위해
@Component : 스프링에서 관리되어야 함
preHandle 메소드 오버라이딩 : 권한 확인
handler : 여러 정보들 가지고 있음
interceptor에서 return false이면 controller로 넘어갈 수 없기 때문에 return true
checkAnnotation 메소드
javascript, html 등 리소스에 대한 것이면 통과
@Auth annotation이 작성되어 있는지 확인
package com.example.intercepter.interceptor;
import com.example.intercepter.annotation.Auth;
import com.example.intercepter.exception.AuthException;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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(request.getRequestURI())
.query(request.getQueryString())
.build()
.toUri();
log.info("request url : {}", url);
boolean hasAnnotation = checkAnnotation(handler, Auth.class);
log.info("has annotation : {}", hasAnnotation);
// 나의 서버는 모두 public으로 동작하는데
// 단, Auth 권한을 가진 요청에 대해서는 세션, 쿠키 등을 보겠다
// 권한을 가지고 있으면
if(hasAnnotation) {
// 권한 체크
String query = uri.getQuery();
if(query.equals("name=steve")){
return true;
}
throw new AuthException(); // 예외 처리
}
return true; // false이면 이후 Controller까지 못감
}
// annotation의 존재 여부 확인
private boolean checkAnnotation(Object handler, Class clazz) {
// javascript, html 등 리소스에 대한 것이면 통과시키기
if(handler instanceof ResourceHttpRequestHandler) {
return true;
}
// annotation check
HandlerMethod handlerMethod = (HandlerMethod)handler;
if(null != handlerMethod.getMethodAnnotation(clazz) || null != handlerMethod.getBeanType().getAnnotation(clazz)) {
// Auth annotation이 있을 때는 true
return true;
}
return false;
}
}
package com.example.intercepter.config;
import com.example.intercepter.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 // final로 선언된 객체들을 생성자에서 주입받을 수 있도록 함
public class MvcConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).addPathPatterns("/api/private/*"); // authInterceptor 등록
// . 이후 -> 검사하고 싶은 패턴에 대해 넣을 수 있음
}
}
package com.example.intercepter.exception;
public class AuthException extends RuntimeException {
}
package com.example.intercepter.handler;
import com.example.intercepter.exception.AuthException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AuthException.class)
public ResponseEntity authException() {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
public 실행 결과
private 실행 결과 (name=steve인 경우)
private 실행 결과 (name=steve가 아닌 경우)