拦截器 应用详解--SpringMVC
在实际项目中,拦截器的使用是非常普遍的,例如在购物网站中通过拦截器可以拦截未登录的用户,禁止其购买商品,或者使用它来验证已登录用户是否有相应的操作权限等,Spring MVC提供了拦截器功能,通过配置即可对请求进行拦截处理。
拦截器的定义:
要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义。一种是通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义;另一种是通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
以实现HandlerInterceptor接口的定义方式为例,自定义拦截器类的代码如下所示:
public class LoginInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return false; } }
关于这三个方法的具体描述如下:
(1)preHandler()方法:该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。
(2)postHandle()方法:该方法会在控制器方法调用之后,其解析试图之前执行。可以通过此方法对请求域中的模型和试图做出进一步的修改。
(3)afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过次方法实现一些资料清理、记录日志信息等工作。
拦截器的配置:
要使自定义的拦截器类生效,还需要在Spring MVC的配置文件中进行配置,配置代码如下:
<!-- 配置拦截器 --> <mvc:interceptors> <!-- 使用bean直接定义在<mvc:interceptors>下面的Interceptor将拦截所有请求 --> <bean class="com.neuedu.interceptor.LoginInterceptor" /> <!-- 拦截器1 --> <mvc:interceptor> <!-- 配置拦截器作用的路径 --> <mvc:mapping path="/**" /> <!-- 配置不需要拦截器作用的路径 --> <mvc:exclude-mapping path="" /> <!-- 定义在<mvc:interceptor>下面的,表示对匹配路径的请求才进行拦截 --> <bean class="com.neuedu.interceptor.interceptor1" /> </mvc:interceptor> <!-- 拦截器2 --> <mvc:interceptor> <mvc:mapping path="/hello" /> <bean class="com.neuedu.interceptor.interceptor2" /> </mvc:interceptor> </mvc:interceptors>
在上述代码中,<mvc:interceptors>元素用于配置一组拦截器,其子元素<bean>中定义是全局拦截器,它会拦截所有的请求;而<mvc:interceptor>元素中定义的是指定路径的拦截器,它会对指定路径下的请求生效。<mvc:interceptor>元素的子元素<mvc:mapping>用于配置拦截器作用的路径,该路径在其属性path中定义。如上述代码path的属性值“/**”表示拦截所有路径,“/hello”表示拦截所有以“/hello”结尾的路径。如果在请求路径中包含不需要拦截的内容,还可以通过<mvc:exclude-mapping>元素进行配置。
需要注意的是,<mvc:interceptor>中的子元素必须按照上述代码的配置顺序进行编写,即<mvc:mapping> -> <mvc:exclude-mapping> -> <bean>的顺序,否则文件会报错。
单个拦截器的执行流程如下:
程序首先会执行拦截器类中的preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行;在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应;在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。
多个拦截器的执行流程:
当多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。
假设有两个拦截器Interceptor1和interceptor2,并且在配置文件中,Interceptor1拦截器配置在前。
接下来我们通过拦截器来完成一个用户登录权限验证的案例。
本案例中,只有登陆后的用户才能访问系统中的主页面,如果没有登录系统而直接访问主页面,则拦截器会将请求拦截,并转发到登录页面,同时在登录页面中给出提示信息。如果用户名或密码错误,也会在登录页面给出相应的提示信息。当已登录的用户在系统主页中单击“退出”链接时,系统同样会回到登录页面。
(1)在eclipse中新建一个动态web项目,在lib目录下导入相关Sping MVC的JAE包,搭建Spring MVC的环境。
(2)在src目录下,创建一个com.neuedu.pojo包,并在包中创建User类。在User类中,代码如下:
package com.neuedu.pojo; public class User { private Integer id;//id private String username;//用户名 private String password;//密码 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
(3)在src目录下,新建com.neuedu.controller包,创建控制器类UserController,并在该类中定义向主页跳转、向登录页面跳转、执行用户登录等操作的方法,代码如下:
package com.neuedu.controller; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.neuedu.pojo.User; @Controller public class UserController { /* * 向用户登录页面跳转 * */ @RequestMapping(value="/login",method=RequestMethod.GET) public String toLogin() { return "login"; } /* * 用户登录 * */ @RequestMapping(value="/login",method=RequestMethod.POST) public String login(User user,Model model,HttpSession session) { //获取用户名和密码 String username = user.getUsername(); String password = user.getPassword(); //此处模拟从数据库中获取用户名和密码后进行判断 if(username != null && username.equals("wangyifei") && password != null && password.equals("123456")) { //将用户对象添加到Session session.setAttribute("USER_SESSION", user); //向主页面跳转 return "main"; } model.addAttribute("msg","用户名或密码错误,请重新登录!"); return "login"; } /* * 向用户主页面跳转 * */ @RequestMapping(value="/main") public String toMain() { return "main"; } /* * 退出登录 * */ @RequestMapping(value="/logout") public String logout(HttpSession session) { //清除Session session.invalidate(); //向登录页面跳转 return "login"; } }
(4)在src目录下,新建com.neuedu.interceptor包,创建拦截器类LoginInterceptor,代码如下:
package com.neuedu.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.neuedu.pojo.User; public class LoginInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取请求的URL String url = request.getRequestURI(); //URL:除了login.jsp是可以公开访问的,其他的URL都进行拦截控制 if(url.indexOf("/login")>=0) { return true; } //获取Session HttpSession session = request.getSession(); User user = (User)session.getAttribute("USER_SESSION"); //判断Session中是否有用户数据,如果有,则返回true,继续向下执行 if(user != null) { return true; } //不符合条件的给出提示信息,并转发到登录页面 request.setAttribute("msg","您还没有登录,请先登录!"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } }
(5)配置文件springmvc-config.xml代码如下所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 定义组件扫描器,指定需要扫描的包 --> <context:component-scan base-package="com.neuedu.controller"/> <!-- 配置注解驱动 --> <mvc:annotation-driven /> <!-- 定义视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 设置前缀 --> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- 设置后缀 --> <property name="suffix" value=".jsp" /> </bean> <!-- 配置拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.neuedu.interceptor.LoginInterceptor" /> </mvc:interceptor> </mvc:interceptors> </beans>
(6)在WEB-INF目录下新建jsp文件夹中,创建一个系统主页面main.jsp,代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> 当前用户:${USER_SESSION.username } <a href="${pageContext.request.contextPath }/logout">退出</a> </body> </html>
(7)在jsp文件夹创建一个login.jsp,在页面中编写一个用于实现登录操作的form表单,代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> ${msg } <form action="${pageContext.request.contextPath }/login.action" method="POST"> 用户名:<input type="text" name="username" /><br /> 密 码: <input type="password" name="password" /><br /> <input type="submit" value="登录" /> </form> </body> </html>
(8)web.xml文件配置代码如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>interceptor</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- 配置 DispatcherServlet 前端控制器 --> <servlet> <servlet-name>hello</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 在此处调用 springmvc.xml 文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-config.xml</param-value> </init-param> <!-- 在 tomcat 服务器启动的时候,最先加载它 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping> </web-app>
(9)整个项目目录结构如下:
(10)将项目发布到Tomcat服务器并启动,在浏览器中访问地址 http://localhost:8080/interceptor/main.action,效果如下:
未登录,不能直接访问主页面!
输入错误用户名“java”和密码“123456”,给出提示!
输入正确用户名“wangyifei”和密码“123456”,成功进入主页面!
转载请标明出处,谢谢~