1. 程式人生 > >SpringBoot之HandlerInterceptor攔截器的使用 ——(一)

SpringBoot之HandlerInterceptor攔截器的使用 ——(一)

HandlerInterceptor簡介

攔截器我想大家都並不陌生,最常用的登入攔截、或是許可權校驗、或是防重複提交、或是根據業務像12306去校驗購票時間,總之可以去做很多的事情。
我仔細想了想
這裡我分三篇部落格來介紹HandlerInterceptor的使用,從基本的使用、到自定義註解、最後到讀取body中的流解決無法多次讀取的問題。

1、定義實現類

定義一個Interceptor 非常簡單方式也有幾種,我這裡簡單列舉兩種
1、類要實現Spring 的HandlerInterceptor 介面
2、類繼承實現了HandlerInterceptor 介面的類,例如 已經提供的實現了HandlerInterceptor 介面的抽象類HandlerInterceptorAdapter

這裡博主用的是第二種方法繼承HandlerInterceptorAdapter

2、HandlerInterceptor方法介紹


    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler
, ModelAndView modelAndView) throws Exception;
void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
  • preHandle:在業務處理器處理請求之前被呼叫。預處理,可以進行編碼、安全控制、許可權校驗等處理;
  • postHandle:在業務處理器處理請求執行完成後,生成檢視之前執行。後處理(呼叫了Service並返回ModelAndView,但未進行頁面渲染),有機會修改ModelAndView (這個博主就基本不怎麼用了);
  • afterCompletion:在DispatcherServlet完全處理完請求後被呼叫,可用於清理資源等。返回處理(已經渲染了頁面);

接下來讓我們來實現一個登陸 and 訪問許可權校驗的攔截器吧

攔截器實現

  • 新建TestFilter
package com.xxx.core.filter;

import com.xxx.common.exception.FastRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class TestFilter extends HandlerInterceptorAdapter {
    private final Logger logger = LoggerFactory.getLogger(TestFilter.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        logger.info("request請求地址path[{}] uri[{}]", request.getServletPath(),request.getRequestURI());
        //request.getHeader(String) 從請求頭中獲取資料
        //從請求頭中獲取使用者token(登陸憑證根據業務而定)
        Long userId= getUserId(request.getHeader("H-User-Token"));
        if (userId != null && checkAuth(userId,request.getRequestURI())){
            return true;
        }
        //這裡的異常是我自定義的異常,系統丟擲異常後框架捕獲異常然後轉為統一的格式返回給前端, 其實這裡也可以返回false
        throw new FastRuntimeException(20001,"No access");
    }

    /**
     * 根據token獲取使用者ID
     * @param userToken
     * @return
     */
    private Long getUserId(String userToken){
        Long userId = null;
        return userId;
    }

    /**
     * 校驗使用者訪問許可權
     * @param userId
     * @param requestURI
     * @return
     */
    private boolean checkAuth(Long userId,String requestURI){
        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 {}
}


  • 新建WebAppConfigurer 實現WebMvcConfigurer介面

其實以前都是繼承WebMvcConfigurerAdapter類 不過springBoot2.0以上 WebMvcConfigurerAdapter 方法過時,有兩種替代方案:
1、繼承WebMvcConfigurationSupport
2、實現WebMvcConfigurer
但是繼承WebMvcConfigurationSupport會讓Spring-boot對mvc的自動配置失效。根據專案情況選擇。現在大多數專案是前後端分離,並沒有對靜態資源有自動配置的需求所以繼承WebMvcConfigurationSupport也未嘗不可。

@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 可新增多個
        registry.addInterceptor(new TestFilter()).addPathPatterns("/**");
    }

    ....
}

其實下面還有很多方法我這裡就省虐了,過濾器可以新增多個,可以指定Path,這裡的/**是對所有的請求都做攔截。

是否感覺配置這個地址是不特別方便?下一篇部落格介紹2.0版本引入註解來協助完成一系列的攔截任務。