스프링 입문
[스프링 입문] 6.4 Filter - Intercepter 활용 (2)
코딩펭귄
2024. 1. 10. 11:57
Interceptor
- Filter와 유사한 형태로 존재
- Spring Context에 등록됨 (Filter - web application에 등록)
- AOP와 유사한 기능 제공
- 인증단계 처리, Logging 하는 데 사용
- 이를 선/후처리 하며 Service business logic과 분리시킴 -> 서비스 인증안된것들 걸러내는 용도
예제
public controller : 아무런 권한이 없는 사용자, 즉 모두가 들어올 수 있도록 = openAPI 형태
private controller : 내부사용자, 즉 세션이 인증된 사용자만 넘김
이 둘(public / private)의 권한차이를 주는 방법 : Interceptor에서 메소드/컨트롤러에 @Auth 어노테이션이 붙어있으면 세션을 검사해서, 있을때만 통과시키고 없을때는 통과시키지 않음
특정권한을 Interceotor에서 검사를 함
annotation 패키지
package com.example.interceptor.annotation;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Auth {
}
config 패키지
package com.example.interceptor.config;
import com.example.interceptor.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 한 순서대로 진행, depth두고 활용할 수 있음
registry.addInterceptor(authInterceptor).addPathPatterns("/api/private/*"); //검사하고싶은 패턴 넣을수있음
}
}
controller 패키지
package com.example.interceptor.controller;
import com.example.interceptor.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 //log를 작성위해
public class PrivateController {
@GetMapping("/hello")
public String hello(){
log.info("private hello controller");
return "private hello";
}
}
package com.example.interceptor.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";
}
}
exception 패키지
package com.example.interceptor.exception;
public class AuthException extends RuntimeException{
}
handler 패키지
package com.example.interceptor.handler;
import com.example.interceptor.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 // AuthException 받아서
public class GlobalExceptionHandler {
@ExceptionHandler(AuthException.class)
public ResponseEntity authException(){ //UNAUTHORIZED라는 http status (401에러 내림)
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
interceptor 패키지
package com.example.interceptor.interceptor;
import com.example.interceptor.annotation.Auth;
import com.example.interceptor.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 //Spring에 의해 관리돼야하기 때문에 붙이는 어노테이션
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")){ //steve인 경우에만 들어옴
return true;
}
throw new AuthException(); //권한이 없으면 exception 던짐 -> handler가 받음
}
return true; // 이면 Interceptor를 넘어서서 안의 로직을 탐
}
//어노테이션이 달려있는지 보는 용도
private boolean checkAnnotation(Object handler, Class clazz){
// resource javascript, html, 에 대한 요청일때는 무조건 통과시킴
if( handler instanceof ResourceHttpRequestHandler){
return true; //통과시킴
}
//annotation check
HandlerMethod handlerMethod = (HandlerMethod) handler;
if(null != handlerMethod.getMethodAnnotation(clazz) || null != handlerMethod.getBeanType().getAnnotation(clazz)){
return true; // Auth annotation가지고있는경우 무조건 true
}
return false;
}
}