zoukankan      html  css  js  c++  java
  • 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

    结尾
    其他类型的实现,可以自行实现。

     
  • 相关阅读:
    Top 10 Product Manager Skills To Boost Your Resume In 2021
    大数据知识梳理
    B端产品如何设计权限系统?
    华三盒式交换机MAC、ARP、Route性能表项参数查询
    中了传说中的挖矿病毒
    SqlServer 2019 事务日志传送
    docker中生成的pdf中文是方框的解决方案
    The Live Editor is unable to run in the current system configuration
    2021 面试题大纲
    五分钟搞定Docker安装ElasticSearch
  • 原文地址:https://www.cnblogs.com/exmyth/p/14294461.html
Copyright © 2011-2022 走看看