第十一章 拦截器、过滤器

时间:2021-6-7 作者:qvyue

target

掌握拦截器的定义和配置
掌握拦截器的执行流程
掌握使用拦截器实现用户登录
了解过滤器的使用场景及过滤器的实现
理解过滤器、拦截器的执行顺序
了解过滤器、拦截器的区别

在开发一个网站时可能有这样的需求:某些页面只希望几个特定的用户浏览。对于这样的访问权限控制,应该如何实现呢?拦截器就可以实现上述需求。拦截器是Spring MVC框架中重要的组成部分。

Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

1. 拦截器的定义

在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,通过实现 HandlerInterceptor 接口 可以定义一个拦截器。

package com.lee.springmvc.interceptor;

public class TestInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
         System.out.println("preHandle方法在控制器的处理请求方法调用之前执行");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
    }

}

在上述拦截器的定义中实现了 HandlerInterceptor 接口,并实现了接口中的 3 个方法。有关这 3 个方法的描述如下。

  • preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。

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

  • afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。

2. 拦截器的配置

让自定义的拦截器生效需要在 Spring MVC 的配置文件中进行配置,配置示例代码如下:


在上述示例代码中, 元素用于配置一组拦截器,其子元素 定义的是全局拦截器,即拦截所有的请求。

元素中定义的是指定路径的拦截器,其子元素 用于配置拦截器作用的路径,该路径在其属性 path 中定义。

如上述示例代码中,path 的属性值/**表示拦截所有路径,/gotoTest表示拦截所有以/gotoTest结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 子元素进行配置。

需要注意的是, 元素的子元素必须按照 的顺序配置。

3. 执行流程

3.1 单个拦截器的执行流程

单个拦截器的执行流程如下:

第十一章 拦截器、过滤器

在配置文件中如果只定义了一个拦截器,程序将首先执行拦截器类中的 preHandle方法,如果该方法返回 true,程序将继续执行控制器中处理请求的方法,否则中断执行。如果 preHandle 方法返回 true,并且控制器中处理请求的方法执行后、返回视图前将执行 postHandle 方法,返回视图后才执行 afterCompletion 方法。

下面通过一个应用 springMVC-11 演示拦截器的执行流程,具体步骤如下:

① 将 springmvc相关jar复制到 lib下

② 创建 web.xml

在 WEB-INF 目录下创建 web.xml 文件,该文件中的配置信息如下:

SpringMVC-11index.htmlindex.jspspringDispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc.xml1springDispatcherServlet/

③ 创建控制器类

在 src 目录下创建一个名为 controller 的包,并在该包中创建控制器类 Interceptor Controller,代码如下:

package com.lee.springmvc.controller;

@Controller
public class InterceptorController {

    @RequestMapping("/gotoTest")
    public String gotoTest() {
        System.out.println("正在测试拦截器,执行控制器的处理请求方法中");
        return "test";
    }
}

④ 创建拦截器类

package com.lee.springmvc.interceptor;

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
    }
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
    }
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法在控制器的处理请求方法调用之前执行");
        return true;
    }
}

⑤ 创建配置文件 springmvc.xml

⑥ 创建视图 JSP文件

在 WEB-INF/views 目录下新建 test.jsp






Insert title here
视图
    


部署项目,访问:http://localhost:8080/SpringMVC-11/gotoTest,控制台将打印:

第十一章 拦截器、过滤器

3.2 多个拦截器的执行流程

执行流程如下:

第十一章 拦截器、过滤器

在 Web 应用中通常需要有多个拦截器同时工作,这时它们的 preHandle 方法将按照配置文件中拦截器的配置顺序执行,而它们的 postHandle 方法和 afterCompletion 方法则按照配置顺序的反序执行。

① 创建多个拦截器

创建两个拦截器:

Interceptor1.java:

package com.lee.springmvc.interceptor;

public class Interceptor1 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor1 preHandle 方法执行");
        /** 返回true表示继续向下执行,返回false表示中断后续的操作 */
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor1 postHandle 方法执行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("Interceptor1 afterCompletion 方法执行");
    }
}

Interceptor2.java:

package com.lee.springmvc.interceptor;

public class Interceptor2 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor2 preHandle 方法执行");
        /** 返回true表示继续向下执行,返回false表示中断后续的操作 */
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor2 postHandle 方法执行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("Interceptor2 afterCompletion 方法执行");
    }
}

② 配置拦截器


部署项目,访问:http://localhost:8080/SpringMVC-11/gotoTest,控制台打印:

第十一章 拦截器、过滤器

思考:

访问:http://localhost:8080/SpringMVC-11/gotoTest2,控制台会打印什么?

4. 用户登录权限验证案例

要求如下:只有成功登录的用户才能访问系统的主页面 main.jsp,如果没有成功登录而直接访问主页面,则拦截器将请求拦截,并转发到登录页面 login.jsp。当成功登录的用户在系统主页面中单击“退出”链接时回到登录页面。

创建动态Web项目:SpringMVC-login

① 导jar包

commons-logging-1.2.jar
jackson-annotations-2.10.1.jar
jackson-core-2.10.1.jar
jackson-databind-2.10.1.jar
spring-aop-4.3.9.RELEASE.jar
spring-beans-4.3.9.RELEASE.jar
spring-context-4.3.9.RELEASE.jar
spring-core-4.3.9.RELEASE.jar
spring-expression-4.3.9.RELEASE.jar
spring-web-4.3.9.RELEASE.jar
spring-webmvc-4.3.9.RELEASE.jar

② 创建web.xml

SpringMVC-loginindex.htmindex.jspspringDispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc.xml1springDispatcherServlet/

③ 创建 pojo类

package com.lee.springmvc.bean;

public class User {
  private String uname;
  private String upwd;
  
  // getter、setter略
}

④ 创建控制器类

package com.lee.springmvc.controller;

@Controller
public class UserController {
    /**
     * 登录页面初始化
     */
    @RequestMapping("/toLogin")
    public String initLogin() {
        return "login";
    }
    /**
     * 处理登录功能
     */
    @RequestMapping("/login")
    public String login(User user, Model model, HttpSession session) {
        System.out.println(user.getUname());
        if ("zhangsan".equals(user.getUname())  && "123456".equals(user.getUpwd())) {
            // 登录成功,将用户信息保存到session对象中
            session.setAttribute("user", user);
            // 重定向到主页面的跳转方法
            return "redirect:main";
        }
        model.addAttribute("msg", "用户名或密码错误,请重新登录! ");
        return "login";
    }
    /**
     * 跳转到主页面
     */
    @RequestMapping("/main")
    public String toMain() {
        return "main";
    }
    /**
     * 退出登录
     */
    @RequestMapping("/logout")
    public String logout(HttpSession session) {
        // 清除 session
        session.invalidate();
        return "login";
    }
}

⑤ 创建拦截器类

package com.lee.springmvc.interceptor;

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 获取请求的URL
        String url = request.getRequestURI();
        // login.jsp或登录请求放行,不拦截
        if (url.indexOf("/toLogin") >= 0 || url.indexOf("/login") >= 0) {
            return true;
        }
        // 获取 session
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        if (user != null) {
            return true;
        }
        // 没有登录且不是登录页面,转发到登录页面,并给出提示错误信息
        request.setAttribute("msg", "还没登录,请先登录!");
        request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
        return false;
    }

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        // TODO Auto-generated method stub
    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
        // TODO Auto-generated method stub
    }
}

⑥ 配置拦截器

在 src 下新建springmvc.xml,并配置拦截器:

⑦ 创建 jsp页面

在 WEB-INF/views 目录下创建 login.jsp 和 main.jsp。

mian.jsp:




  
    
welcome
    当前用户:${user.uname }
退出

login.jsp :




  
    
login
    ${msg }
    
      用户名:
密码:

部署应用,访问:http://localhost:8080/SpringMVC-login/main,如图:

第十一章 拦截器、过滤器

当用户没有登录而直接访问系统主页面时请求将被登录拦截器拦截,返回到登录页面,并提示信息。如果用户在用户名框中输入“zhangsan”,在密码框中输入“123456”,单击“登录”按钮后浏览器的显示结果如图 :

第十一章 拦截器、过滤器

5. 过滤器

过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理

通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。

5.1 应用场景

  • 自动登录

  • 统一设置编码格式

  • 访问权限控制

  • 敏感字符过滤等

5.2 创建过滤器

在 web.xml中用 filter标签配置:

myFiltercom.lee.filter.MyFiltermyFilter/*

MyFilter.java

通过实现接口OncePerRequestFilter来创建一个过滤器:

public class MyFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        System.out.println("请求前:" + filterChain.getClass());
        filterChain.doFilter(request, response);
        System.out.println("响应时:" + filterChain.getClass());
    }

}

filterChain.doFilter(request, response):表示放行请求。
filterChain.doFilter(request, response)之前代码:表示放行请求前执行的一些操作。
filterChain.doFilter(request, response)之后代码:表示响应时执行的操作。

5.3 登录案例

public class MyFilter extends OncePerRequestFilter {

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {
  
    HttpSession session = httpServletRequest.getSession();
    if (session.getAttribute("login") == null) { //没有登陆
      httpServletResponse.sendRedirect("/login");
    } else {//已登录
      System.out.println("ip:" + httpServletRequest.getRemoteHost() + ",url:" + httpServletRequest.getRequestURL());
      // 将请求放行
      filterChain.doFilter(servletRequest, servletResponse);
    }
  }

}

5.4 urlPatterns

配置要拦截的资源

  • 以指定资源匹配。例如"/index.jsp"

  • 以目录匹配。例如"/servlet/*"

  • 以后缀名匹配,例如"*.jsp"

  • 通配符,拦截所有web资源。"/*"

    6. 过滤器、拦截器执行顺序

    当客户端的一个请求发送到服务端时,会被过滤器和拦截器所拦截。他们过滤拦截的顺序是:

    第十一章 拦截器、过滤器
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。