一、拦截器的概述
Spring MVC的拦截器(Intercept)与Java Servlet的过滤器(Filter)类型,主要用于拦截用户的请求并做相应的处理,通常在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
1.1 拦截器的定义
在Spring MVC框架中定义一个拦截器需要对拦截器进行定义和配置,定义一个拦截器可以通过两种方式:
- 通过实现HandlerInterceptor接口或者继承HandlerInterceptor接口的实现类来定义
- 通过实现WebRequestInterceptor或者继承WebRequestInterceptor接口的实现类来定义
比如实现HandlerInterceptor接口:
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
实现HandlerInterceptor接口,实现了接口中的3种方法:
- preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回true表示继续向下执行,返回false表示中断后续操作。
- postHandle 方法:该方法在控制器的处理请求调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
- afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
1.2 拦截器的配置
自定义拦截器生效需要在Spring MVC的配置文件中进行配置。
<!--配置拦截器-->
<mvc:interceptors>
<!--配置一个全局拦截器,拦截所有请求-->
<bean class="main.java.springmvc.utils.UserInterceptor"/>
<mvc:interceptor>
<!--配置拦截器作用的路径-->
<mvc:mapping path="/**"/>
<!--配置不需要拦截器作用的路径-->
<mvc:exclude-mapping path=""/>
<!--定义在 <mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截-->
<bean class=""
</mvc:interceptor>
</mvc:interceptors>
- <mvc:interceptors> 用于配置一组拦截器
- <bean >定义的是全局拦截器,拦截所有请求
- <mvc:interceptor>指定路径的拦截器,顺序不能变!
二、拦截器的执行流程
2.1 单个拦截器的执行流程
1. 准备拦截器类
public class UserInterceptor 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方法在控制器的处理请求方法执行完成后执行");
}
}
2. 在springmvc配置文件中配置单个拦截器
<mvc:interceptors>
<bean class="main.java.springmvc.utils.interceptor.UserInterceptor"/>
</mvc:interceptors>
3. 编写测试界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试拦截器执行流程</title>
</head>
<body>
视图
<% System.out.println("视图渲染结束。");%>
</body>
</html>
4. 编写控制器,请求跳转至测试界面
@Controller
public class TestInterceptor {
@RequestMapping("/toTestInterceptor")
public String test() {
System.out.println("正在测试拦截器,执行控制器的处理请求方法中");
return "testinterceptor";
}
}
5. 结果
从结果可以看出执行流程,单个拦截器中首先执行拦截器中的preHandle方法,方法返回true,程序继续执行控制器中处理请求的方法,然后在返回视图前执行postHandle方法,返回视图后才执行afterCompletion方法。
2.2 多个拦截器的执行了流程
1. 在创建两个两个拦截器Interceptor1 和Interceptor2
public class Interceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor1 preHandle方法执行中");
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方法执行中");
}
}
2. springmvc配置文件中配置多个拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="main.java.springmvc.utils.interceptor.Interceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/toTestInterceptor"/>
<bean class="main.java.springmvc.utils.interceptor.Interceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
3. 访问请求进行测试
由结果可知,在程序中有多个拦截器同时工作时,他们的preHandle方法按照配置文件中拦截器的配置顺序执行,而postHandle方法、afterCompletion方法按照反序执行。
三、简单实例–用户登陆权限验证
1. 创建实体类User
public class User {
private String name;
private String password;
}
2. 创建服务层UserService接口和实现类
public interface UserService {
public Boolean isLogin(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public Boolean isLogin(User user) {
if ("zhangsan".equals(user.getName()) && "123456".equals(user.getPassword())){
return true;
}else {
return false;
}
}
}
3. 编写控制器类
@Controller
@RequestMapping(value = "/index")
public class UserContrloller {
@Autowired
private UserService userService;
private static final Log logger = LogFactory.getLog(UserContrloller.class);
@RequestMapping("/isLogin")
public String isLogin(User user, Model model, HttpSession session) {
if (userService.isLogin(user)){
session.setAttribute("user",user);
logger.info("登陆成功");
return "redirect:/index/toSuccess";
}else {
logger.info("登陆失败");
model.addAttribute("loginfail", "用户名或密码错误,请重新登陆");
return "login";
}
}
@RequestMapping(value = "/toLogout")
public String logout(HttpSession session) {
session.invalidate();
return "login";
}
@RequestMapping(value = "/toLogin")
public String toLogin() {
return "login";
}
@RequestMapping(value = "/toSuccess")
public String toSuccess() {
return "loginsuccess";
}
}
4. 前端登陆界面和登陆成功界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登陆界面</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/index/isLogin" method="post">
<table>
<tr>
<td colspan="2"> 登陆界面 </td>
</tr>
<tr>
<td>用户名: <input type="text" name="name" placeholder="请输入用户名"/> </td>
<td> 密码:<input type="password" name="password" placeholder="请输入密码"/> </td>
</tr>
<tr>
<td colspan="2"> <input type="submit" value="登陆"> </td>
</tr>
</table>
${msg}
${loginfail}
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login success</title>
</head>
<body>
hello ${user.name} 欢迎回来<br>
<a href="${pageContext.request.contextPath}/index/toLogout">退出</a>
</body>
</html>
5. 登录拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
StringBuffer url = request.getRequestURL();
if(url.indexOf("/toLogin")>=0 || url.indexOf("/isLogin")>=0){
return true ;
}
HttpSession session = request.getSession();
Object obj = session.getAttribute("user");
if (obj != null) {
return true;
}
request.setAttribute("msg", "没有登录,请先登录");
request.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(request, response);
return false;
}
}
6. springmvc配置文件中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="main.java.springmvc.utils.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
7. 结果查看
登录界面,放行进行登陆
登陆请求isLogin放行,转到登录成功界面:
退出到登陆界面,退出前session值还有存在:
退出后session值注销掉,直接访问成功界面效果:
至此,简单的登录权限验证拦截器完成
|