LoginSignup
40
44

More than 5 years have passed since last update.

SpringBootの特定のAnnotationが付与されたControllerのメソッドに対して事前処理を行う

Last updated at Posted at 2016-07-06

概要

SpringBootで構築しているWebアプリケーションで、Controllerに対して共通処理を行いたい。
主に簡単な認証処理で、ログイン状態であればControllerでの処理、ログイン状態でなければログイン画面へ遷移させたい。
URLパスやパッケージベースでなく、Annotationで管理したい。

方法

HandlerInterceptor を使うことで実装が可能になる。

HandlerInterceptorをimplementしたクラスでは preHandle postHandle afterCompletion を実装する必要がある。
それぞれ、
- preHandle → Controllerにリクエストが行く前に呼ばれる
- postHandle → ビューのレンダリング前に呼ばれる。RestControllerでは呼ばれない。
- afterCompletion → ビューのレンダリング後に呼ばれる。RestControllerでも呼ばれる。

となっている。

実装したクラスを作成し、@Configuration を付与し、全てのパッケージにおいて有効にしておく。

Interceptor



/**
 * login check
 */
@Log4j
public class SampleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // trueであれば通す
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}


Configuraiton


/**
 * configuration for app
 */
@Configuration
public class WebApplicationConfiguration {
    @Bean
    public SampleInterceptor sampleInterceptor() {
        return new SampleInterceptor();
    }

    @Bean
    public MappedInterceptor interceptor() {
        return new MappedInterceptor(new String[]{"/**"}, sampleInterceptor());
    }
}

有効範囲をContorollerのAnnotationで制御する

preHandleでログインチェックを行うが、このままでは 引数の HttpServletRequest からrequestURLを取り出し、特定のURLでは認証チェックを除外する、といった処理になるが煩雑なのでAnnotationを自作する。
仮に@NonAuthとする。method単位につけられるように、@Target(ElementType.METHOD) としておく。

Annotation

/**
 * non require auth
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NonAuth {
}

このAnnotationがついている場合には、認証チェックを走らせないようにする。
HandleIntercepter内で、実行されるContorollerClassのメソッドのAnnotationを取得するには、preHandleの引数の Object handlerorg.springframework.web.method.HandlerMethod;キャストしてからgetMethodを呼び出す。
その後、org.springframework.core.annotation.AnnotationUtils で、当該Annotationが付与されてるかチェックしてあげればよい。

Interceptor

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

        HandlerMethod hm = (HandlerMethod) handler;
        Method method = hm.getMethod();
        NonAuth annotation = AnnotationUtils.findAnnotation(method, NonAuth.class);
        if (annotation != null) {
            log.info(String.format("exclude login check %s", requestURI));
            return true;
        }
}

しかし、このままだと、静的ファイルへのアクセス時にも呼び出しが行われ、その時はHandlerMethodへのキャスト時にClassCastExceptionが発生してしまうので、js/cssが含まれていたら除外している。
多分ここはもっとマシな方法がある。

Interceptor


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        if (isLoginCheckPath(requestURI) == false) {
            return true;
        }

        HandlerMethod hm = (HandlerMethod) handler;
        Method method = hm.getMethod();
        NonAuth annotation = AnnotationUtils.findAnnotation(method, NonAuth.class);
        if (annotation != null) {
            log.info(String.format("exclude login check %s", requestURI));
            return true;
        }
        private boolean isLoginCheckPath(String requestUri) {
            return requestUri.contains("js/") == false && requestUri.contains("css/") == false;
        }
}

↑上記はやはりイマイチだった。
静的リソースの場合は、handlerが org.springframework.web.servlet.resource.ResourceHttpRequestHandlerのインスタンスになるので、その場合も通してあげればよい。

interceptor


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

        //静的リソースの場合は認証不要
        if (handler instanceof ResourceHttpRequestHandler) {
              return true;
        }

        // @NonAuthがついてるメソッドは認証不要
        HandlerMethod hm = (HandlerMethod) handler;
        Method method = hm.getMethod();
        NonAuth annotation = AnnotationUtils.findAnnotation(method, NonAuth.class);
        if (annotation != null) {
            log.info(String.format("exclude login check %s", requestURI));
            return true;
        }

    //認証済みかチェックする処理
}
40
44
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
44