zoukankan      html  css  js  c++  java
  • MVC拦截器

    什么是拦截器

      Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。在SpringMVC中通过实现HandlerInterceptor接口实现自定义拦截器类。

    拦截器可以做什么

      拦截器可以用于权限验证、解决乱码、操作日志记录、性能监控、异常处理

    MVC拦截器执行流程

      

    自定义拦截器

    public class MyInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
             //对浏览器的请求进行放行处理
            System.out.println("preHandle====");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle====");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
            System.out.println("afterCompletion====");
        }
    }
    View Code

    接口中实现的三个方法

      preHandle() 方法:该方法会在控制器方法前执行,其返回值表示是否中 断后续操作。当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控 制器类中的方法执行等)。

      postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执 行。可以通过此方法对请求域中的模型和视图做出进一步的修改。

      afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之 后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。

    拦截器配置

    <mvc:interceptors>
            <mvc:interceptor>
          <!--拦截器映射的URL-->
                <mvc:mapping path="/**"/>
               <!--配置拦截器类-->
                <bean class="com.cmy.inter.InterTest"></bean>
            </mvc:interceptor>
    </mvc:interceptors>
    View Code

    拦截器和过滤器的区别

    1、过滤器

      依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。

    2、拦截器

      依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。

    使用拦截器解决CSRF攻击

      1、CSRF:CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

      2、CSRF可以做什么:你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。

      

    解决案例

      导入依赖:

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.3.2</version>
    </dependency>
    View Code

      编写拦截器:

    package com.cmy.inter;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    public class InterTest implements HandlerInterceptor {
        @Override
        //调用控制器方法之前调用的函数
        //鉴权 过滤敏感词的操作
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
            //获取session对象
            HttpSession session=httpServletRequest.getSession();
            //获取内部的安全随机数
            String session_csrf = String.valueOf(session.getAttribute("_csrf"));
            //根据用户请求传递而来的安全随机数
            String request_csrf = httpServletRequest.getParameter("_csrf");
            System.out.println(session_csrf+"===session");
            System.out.println(request_csrf+"===request");
            //移除安全随机数
            //session.removeAttribute("_csrf");
            //判断非空 安全的情况下
            if (StringUtils.isNotBlank(session_csrf)&&StringUtils.isNotBlank(request_csrf)&&session_csrf.equals(request_csrf)){
                return true;
            }else{
                httpServletResponse.setContentType("text/html;charset=utf-8");
                httpServletResponse.setStatus(403); //处理跨域状态码
                httpServletResponse.getWriter().write("请不要重复提交请求,如有疑问,请联系客服");
                return false;
            }
        }
    
        @Override
        //调用完控制器方法之后调用的函数
        //记录用户日志的功能
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle控制器方法之后=====2222222222");
        }
    
        @Override
        //数据渲染到视图结束后调用的函数
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            System.out.println("afterCompletion渲染视图之后=====3333333333");
        }
    }
    View Code

      spring-mvc配置:

    <mvc:interceptors>
            <mvc:interceptor>
          <!--拦截器映射的URL-->
                <mvc:mapping path="/**"/>
               <!--配置拦截器类-->
                <bean class="com.cmy.inter.InterTest"></bean>
            </mvc:interceptor>
    </mvc:interceptors>
    View Code

       login.jsp页面:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <%
            //安全随机数
            SecureRandom random = new SecureRandom();
            //随机种子
            random.setSeed(8738);
            double _csrf = random.nextDouble();
            session.setAttribute("_csrf", _csrf);
        %>
    </head>
    <body>
    <form action="/login/index" method="post">
        <input type="text" name="username"/>
        <input type="password" name="password" />
        <input type="submit" name="submit"/>
        <input type="hidden" name="_csrf" value="<%=_csrf%>">
    </form>
    
    </body>
    </html>
    View Code

      index.jsp页面:

    <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    欢迎${username}
    
    </body>
    </html>
    View Code

      结果:

      

      

    单个拦截器的执行流程

      在运行程序时,拦截器的执行是有一定顺序的,该顺序与配置文件中所定义的拦截器的顺序相关。
      单个拦截器,在程序中的执行流程如下图所示:

        

        1.程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行。

        2.在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应。

        3.在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。

      实例

        控制器:

    @Controller 
    public class HelloController {
        @RequestMapping("/hello") 
        public String Hello() {
             System.out.println("Hello!"); return "success";
         }
    }
    View Code

        拦截器:

    public class CustomeInterceptor implements HandlerInterceptor {
      @Override
      public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)
    throws Exception {
        System.out.println("CustomInterceptor....preHandle");
        //对浏览器的请求进行放行处理
        return true;
    }
    
      @Override
      public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView)
    throws Exception {
        System.out.println("CustomInterceptor....postHandle");
    }
    
      @Override
      public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e)
    throws Exception {
        System.out.println("CustomInterceptor....afterCompletion");
    }
    }
    View Code

        spring-mvc配置:

    <mvc:interceptors>
            <mvc:interceptor>
          <!--拦截器映射的URL-->
                <mvc:mapping path="/**"/>
               <!--配置拦截器类-->
                <bean class="com.cmy.inter.InterTest"></bean>
            </mvc:interceptor>
    </mvc:interceptors>
    View Code

    多个拦截器的执行流程 

      多个拦截器(假设有两个拦截器Interceptor1和Interceptor2,并且在配置文件中, Interceptor1拦截器配置在前),在程序中的执行流程如下图所示:

        

      实例

        第一个拦截器:

    /**
    * @author mz
    * @version V1.0
    * @Description: 第一个拦截器
    */
    public class Intercptor1 implements HandlerInterceptor {
      @Override
      public boolean preHandle(HttpServletRequest httpServletRequest,
    HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("Interceptor1....preHandle");
        return true;
    }
    
      @Override
      public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor1....postHandle");
    }
    
      @Override
      public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("Interceptor1....afterCompletion");
    }
    View Code

        第二个拦截器:

    /**
    * @author mz
    * @version V1.0
    * @Description: 第二个拦截器
    */
    public class Interceptor2 implements HandlerInterceptor {
      @Override
      public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("Interceptor2....preHandle");
        return true;
      }
    
      @Override
      public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor2....postHandle");
      }
    
      @Override
      public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("Interceptor2....afterCompletion");
      }
    }
    View Code

        spring-mvc配置文件:

    <!--拦截器1-->
    <mvc:interceptor>
        <!--配置拦截器的作用路径-->
        <mvc:mapping path="/**"/>
        <!--定义在<mvc:interceptor>下面的表示匹配指定路径的请求才进行拦截-->
        <bean class="com.ma.interceptor.Intercptor1"/>
    </mvc:interceptor>
    <!--拦截器2-->
    <mvc:interceptor>
        <mvc:mapping path="/hello"/>
        <bean class="com.ma.interceptor.Interceptor2"/>
    </mvc:interceptor>
    View Code
  • 相关阅读:
    boost::ASIO的异步方式
    C++ 类构造函数初始化列表
    C++11 shared_ptr(智能指针)详解
    C/C++中静态成员变量和静态成员函数的含义与不同用法
    静态局部变量
    GDB入门教程之如何使用GDB启动调试
    Qt的QWaitCondition了解一下吧
    Qt信号量QSemaphore认识一下
    瞧一瞧Qt的QMutex
    Qt的读写锁QReadWriteLock要怎么玩?
  • 原文地址:https://www.cnblogs.com/wnwn/p/11839873.html
Copyright © 2011-2022 走看看