본문 바로가기
개발 이론/Spring

[Spring] HandlerMethod

by dal_been 2023. 11. 16.
728x90

스프링 시큐리티 기능을 인터셉터로 구현할 수 있다는 거를 공부하다가 HandlerMethod개념이 나와서 공부해본다...

아니 코드 5줄 읽으면 공부해야하는 개념이 2-3개씩 나오는게 정상이겠지..??

 

 

HandlerMethod

 

@RequestMappin과 같은 @GetMapping, @PostMapping등 붙은 메서드의 정보를 추상화한 객체이다.

그자체가 실행가능한 객체가 아니라 메소드를 실행하기 위해 필요한 정보를 담고 있는 객체이다

 

빈객체, 메서드 메타정보, 메소드 파라미터 정보, 메서도 어노테이션 정보, 메소드 리턴값 메타정보

 

디스패처 서블릿은 애플리케이션 실행될때 모든 컨트롤런의 빈의 메서드를 살펴서 매핑 후보가 되는 메서드를 추출한뒤, 이를 HandlerMethod형태로 저장해둔다. 그리고 실제 요청이 들어오면 저장해둔 목록에서 요청 조건에 맞는 HandlerMethod를 참조해서 매핑되는 메서드를 실행한다

 

 

만약 HTTP요청이 오면 RequestMappingHandlerMapping이 HandlerMethod객체로 변환하는 작업을 하거나 해당 요청에 매핑되는 HandlerMethod를 반환한다.

 

여기서 잠깐 디스패처 서블릿 과정을 보자 그러면 밑에서 설명할 HandlerMethod를 이용한 권한여부확인을 조금더 잘 이해할 수 있을 것이다

 

 

디스패처 서블릿은 요청을 처리할 핸들러(컨트롤러)를 찾고 해당 객체의 메서드를 호출한다. 이때 어느 컨트롤러가 맞는지를 식별하는게 HadlerMapping이다. @Controller를 앞서 얘기한 RequestMappingHandlerMapping가 처리한다. 작성된 모든 컨트롤러를 찾고 HashMap에 관리한다. 여기서 처리할 대상은 HandlerMethod객체로 컨트롤러, 메서드 등을 갖고 있다.

 

따라서 HashMap에서 요청을 처리할 HandelrMethod를 찾고 HandlerExecutionChain으로 감싸서 반환하다

-> 컨트롤러 전 인터셉터등을 처리하기 위해서

 

이후 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달(핸들러 어댑터가 컨트롤러로 요청을 위임함)

 

일단 간단하게 디스패처 서블릿에 동작과 HandlerMethod가 어디서 튀어나온 개념인지 알아보았다.

이 개념을 이용해서 스프링 시큐리티의 권한이 있는지 와 같은 @PreAuthorize와 비슷하게 구현할 수 있다

 

 

권한 확인 인터셉터

 

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {

    boolean required() default true;

    boolean admin() default false;
}

 

public class RoleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        // handler를 HandlerMethod 타입으로 다운캐스팅 / 요청을 처리할 메소드
        HandlerMethod method = (HandlerMethod)handler;
        
        // 메소드에 붙은 어노테이션 참조
        Role role = method.getMethodAnnotation(Role.class);
        
        if(role == null) {
            // 메소드에 붙은 role 어노테이션이 없다면 권한 제약이 없다는 것으로 생각하고 통과
            return true;
        }

		// 로그인 시에 미리 세션에 저장해둔 유저의 MemberType을 꺼냄
        String authority = (String)request.getSession().getAttribute("authority");

        // 어노테이션의 authority 값이 admin인 경우 
        if(role.admin()&&authority.equals(MemberType.admin)){
            return true;
        }

        throw new RuntimeException("No access");
    }
}

 

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final List<HandlerInterceptor> interceptors;
    private final List<HandlerMethodArgumentResolver> resolvers;

    public WebConfig(final List<HandlerInterceptor> interceptors, final List<HandlerMethodArgumentResolver> resolvers) {
        this.interceptors = interceptors;
        this.resolvers = resolvers;
    }

    @Override
    public void addInterceptors(final InterceptorRegistry registry) {
        interceptors.forEach(registry::addInterceptor);
    }
  }

 

 

어노테이션을 만들고 인터셉터로 해당 어노테이션이 있으면 http에  있는 헤더,세션,메서지를 통해 권한을 확인한다.

WebMvcConfigurer에 인터셉터 등록!

 

 

 


https://mangkyu.tistory.com/180