springboot攔截器過濾token,並返回結果及異常處理


package com.xxxx.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ModelAndViewDefiningException;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * 基礎攔截器,通過@Configuration自行配置為Bean,可以配置成多個攔截器。
 *
 * @author obiteaaron
 * @since 2019/12/26
 */
public class BaseInterceptor implements AsyncHandlerInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(BaseInterceptor.class);

    private ApplicationContext applicationContext;

    protected InterceptorPreHandler interceptorPreHandler;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean checkResult = interceptorPreHandler.check(request, response, handler);
        if (!checkResult) {
            postInterceptor(request, response, handler);
            return false;
        } else {
            return true;
        }
    }

    /**
     * 攔截后處理
     */
    protected void postInterceptor(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 如果被攔截,返回信息
        if (((HandlerMethod) handler).getMethodAnnotation(ResponseBody.class) != null) {
            // 返回json
            HandlerMethod handlerMethod = new HandlerMethod(((HandlerMethod) handler).getBean(), ((HandlerMethod) handler).getMethod());
            Object returnValue = interceptorPreHandler.getResponseBody();
            MethodParameter returnValueType = handlerMethod.getReturnValueType(returnValue);
            applicationContext.getBean(RequestMappingHandlerAdapter.class).getReturnValueHandlers();
            RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = findRequestResponseBodyMethodProcessor();
            requestResponseBodyMethodProcessor.handleReturnValue(returnValue, returnValueType, new ModelAndViewContainer(), new ServletWebRequest(request, response));
            // end
        } else {
            // 返回頁面
            HandlerMethod handlerMethod = new HandlerMethod(((HandlerMethod) handler).getBean(), ((HandlerMethod) handler).getMethod());
            String viewName = interceptorPreHandler.getViewName();
            MethodParameter returnValueType = handlerMethod.getReturnValueType(viewName);
            ViewNameMethodReturnValueHandler viewNameMethodReturnValueHandler = findViewNameMethodReturnValueHandler();
            ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();
            // viewNameMethodReturnValueHandler 內的實現非常簡單,其實可以不用這個的,直接new ModelAndViewContainer()就好了。
            viewNameMethodReturnValueHandler.handleReturnValue(viewName, returnValueType, modelAndViewContainer, new ServletWebRequest(request, response));

            // 拋出異常由Spring處理
            ModelMap model = modelAndViewContainer.getModel();
            ModelAndView modelAndView = new ModelAndView(modelAndViewContainer.getViewName(), model, modelAndViewContainer.getStatus());
            throw new ModelAndViewDefiningException(modelAndView);
            // end
        }
    }

    private RequestResponseBodyMethodProcessor findRequestResponseBodyMethodProcessor() {
        RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
        for (HandlerMethodReturnValueHandler value : Objects.requireNonNull(requestMappingHandlerAdapter.getReturnValueHandlers())) {
            if (value instanceof RequestResponseBodyMethodProcessor) {
                return (RequestResponseBodyMethodProcessor) value;
            }
        }
        // SpringMVC的環境下一定不會走到這里
        throw new UnsupportedOperationException("cannot find RequestResponseBodyMethodProcessor from RequestMappingHandlerAdapter by Spring Context.");
    }

    private ViewNameMethodReturnValueHandler findViewNameMethodReturnValueHandler() {
        RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
        for (HandlerMethodReturnValueHandler value : Objects.requireNonNull(requestMappingHandlerAdapter.getReturnValueHandlers())) {
            if (value instanceof ViewNameMethodReturnValueHandler) {
                return (ViewNameMethodReturnValueHandler) value;
            }
        }
        // SpringMVC的環境下一定不會走到這里
        throw new UnsupportedOperationException("cannot find ViewNameMethodReturnValueHandler from RequestMappingHandlerAdapter by Spring Context.");
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void setInterceptorPreHandler(InterceptorPreHandler interceptorPreHandler) {
        this.interceptorPreHandler = interceptorPreHandler;
    }

    public interface InterceptorPreHandler {
        /**
         * @see HandlerInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)
         */
        boolean check(HttpServletRequest request, HttpServletResponse response, Object handler);

        /**
         * 攔截后返回的視圖名稱
         *
         * @see ModelAndView
         * @see ViewNameMethodReturnValueHandler
         */
        String getViewName();

        /**
         * 攔截后返回的對象
         *
         * @see ResponseBody
         * @see RequestResponseBodyMethodProcessor
         */
        Object getResponseBody();
    }
}

 

SpringBoot版本:2.1.6.RELEASE
SpringMVC版本:5.1.8.RELEASE

SpringMVC攔截器
比如說在SpringMVC Web環境下,需要實現一個權限攔截的功能,一般情況下,大家都是實現了org.springframework.web.servlet.AsyncHandlerInterceptor或者org.springframework.web.servlet.HandlerInterceptor接口,從而實現的SpringMVC攔截。而要實現攔截功能,通常都是通過preHandle方法返回false攔截。

攔截器的preHandle
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}

那么攔截后,如果你什么都不做,會直接返回空頁面,頁面上什么也沒有。如果要返回結果,需要自己給response寫數據。簡單的寫法網上很多,這里不贅述,這里將會講解如何通過SpringMVC本身的處理機制在攔截后返回結果。

攔截后返回結果
攔截后返回數據通常是兩種,第一種如果是返回的Restful接口,那么返回一個json數據即可,第二種如果返回的是一個頁面,那么需要返回錯誤頁面(比如無權限頁面)。

SpringMVC的所有結果都是通過HandlerMethodReturnValueHandler接口的實現類返回的,無論是Json,還是View。因此可以通過具體的實現類返回我們想要的數據。與之對應的還有``,這些所有的參數和返回結果的處理器,都定義在RequestMappingHandlerAdapter中,這個Adapter可以從容器中獲取到。這里我們主要用到的只有兩個RequestResponseBodyMethodProcessor和ViewNameMethodReturnValueHandler。

返回純數據
返回純數據,適用於返回Controller的方法通過@ResponseBody標注了。因此需要用到RequestResponseBodyMethodProcessor。
RequestResponseBodyMethodProcessor里面對不同的數據會有不同的處理方式,一般都是處理為json,具體實現可以看HttpMessageConverter的實現類。這里是直接將結果寫到了response中。實現代碼在文末。

返回視圖
返回視圖,適用於返回Controller的方法通過是個String,其實是ViewName。因此需要用到ViewNameMethodReturnValueHandler。

通過查看DispatcherServlet代碼會發現,其實preHandle方法執行在RequestMappingHandlerAdapter執行前,所以沒有ModelAndView生成,因此需要自己向Response里面寫數據。這里只是借助了RequestMappingHandlerAdapter生產需要寫入的數據。然后通過拋出異常ModelAndViewDefiningException,從而將我們的生產的ModeAndView透出給Spring進行渲染DispatcherServlet#processDispatchResult。

實現代碼在文末。

直接使用視圖解析器方法
如果你知道自己的視圖解析器是誰,那么還有一個方法,比如,我用的是Velocity的視圖解析器,Velocity的視圖解析器配置的beanName是velocityViewResolver,因此可以用下面的方法實現。

 

SpringBoot下注冊攔截器:org.springframework.web.servlet.config.annotation.WebMvcConfigurer#addInterceptors

結尾
其他類型的實現,可以自行實現。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM