这是我参与更文挑战的第 20 天,活动详情查看
SpringMVC中的拦截器
有很多场景会使用到拦截器,比如登录认证、身份授权、统一异常处理、统一日志处理,使用拦截器很容易就能实现上述功能,
二、SpringMVC 拦截器简介以及简单使用
1、SpringMVC 拦截器简介
1)SpringMVC 拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似与 servlet 中的 Filter。
2)SpringMVC 中的 Interceptor 拦截请求是通过 HandlerInterceptor 来实现的。
3)在 SpringMVC 中定义一个 Interceptor 非常简单,主要有4种方式:
- 实现 Spring 的 HandlerInterceptor 接口;(本章节案例使用此方式)
- 继承实现了 HandlerInterceptor 接口的类,比如 Spring 已经提供的实现了HandlerInterceptor 接口的抽象类 HandlerInterceptorAdapter;
- 实现 Spring 的 WebRequestInterceptor 接口;
- 继承实现 WebRequestInterceptor 的类;
2、定义拦截器
本章节中所用拦截器都是用实现 HandlerIntercepter 接口方式,创建 LoginInterceptor 类,实现 HandlerInterceptor 接口并重写里面的三个方法。
public class LoginInterceptor implements HandlerInterceptor {
//Handler执行前调用
//应用场景:登录认证、身份授权
//返回值为true则是放行,为false是不放行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
//进入Handler开始执行,并且在返回ModelAndView之前调用
//应用场景:对ModelAndView对象操作,可以把公共模型数据传到前台,可以统一指定视图
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
//执行完Handler之后调用
//应用场景:统一异常处理、统一日志处理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
3、配置拦截器
SpringMVC 拦截器是绑定在 HandlerMapping 中的,即如果某个 HandlerMapping 中配置拦截,则该 HandlerMapping 映射成功的 Handler 会使用该拦截器。
1)针对单个 HandlerMapping 配置
只有通过该处理器映射器查找到的处理器,才能使用该拦截器。
如果现在有两个处理器映射器:其中一个设置了处理器拦截器,另外一个没有设置,如果通过第二个映射器查找到的处理器,是无法使用拦截器的。
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="interceptor" />
</list>
</property>
</bean>
<bean id="interceptor" class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor" />
2)全局拦截器配置
SpringMVC 的全局拦截器配置,其实是把配置的拦截器注入到每个已初始化的HandlerMapping 中
<!-- 配置全局mapping的拦截器 -->
<mvc:interceptors>
<!-- 公共拦截器可以拦截所有请求,而且可以有多个 -->
<bean class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor1" />
<bean class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor2" />
<!-- 如果有多个拦截器,则按照顺序进行配置 -->
<mvc:interceptor>
<!-- /**表示所有URL和子URL路径 -->
<mvc:mapping path="/test/**" />
<!-- 特定请求的拦截器只能有一个 -->
<bean class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor3" />
</mvc:interceptor>
</mvc:interceptors>
这里需要注意的是:如果有多个拦截器,那么配置在 springmvc.xml 中最上面的拦截器,拦截器优先级最高。
三、拦截器实现登录认证 demo
这里使用拦截器模拟登录认证,只是模拟一下登录认证的大体流程,并没有实现真正的认证,目的在于让读者了解拦截器的具体应用。
1、具体需求
- 拦截器对访问的请求 URL 进行拦截校验;
- 如果请求的 URL 是公开地址(无需登录就可以访问的 URL,具体指的就是保护 login 字段的请求 URL),采取放行。如果用户 session 存在,则放行;
- 如果用户 session 中不存在,则跳转到登录页面。
2、登录 jsp 页面:login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录页面</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/login" method="post">
<table align="center" border="1" cellspacing="0">
<tr>
<td>用户名:<input type="text" name="username"/></td>
</tr>
<tr>
<td>密 码:<input type="text" name="password"/></td>
</tr>
<tr>
<td><input type="submit" value="登录"/></td>
</tr>
</table>
</form>
</body>
</html>
3、LoginController.java
@Controller
public class LoginController {
/**
* 显示登录页面
* @return
*/
@RequestMapping("/loginPage")
public String loginPage() {
return "login/login";
}
/**
* 登录
* @param session
* @param username
* @param password
* @return
*/
@RequestMapping("/login")
public String login(HttpSession session, String username, String password) {
// 把用户信息保存到session中
session.setAttribute("username", username);
// 重定向到商品列表页面
return "redirect:/queryItem";
}
// 退出
@RequestMapping("/logout")
public String logout(HttpSession session) {
//清空session
session.invalidate();
// 重定向到登录页面
return "redirect:/loginPage";
}
}
4、拦截器:LoginInterceptor.java
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取请求的URI
String requestURI = request.getRequestURI();
System.out.println(requestURI);
// 1.如果请求的URL是公开地址(无需登录就可以访问的URL),采取放行。
if (requestURI.indexOf("login") > -1) {
return true;
}
// 2.如果用户session存在,则放行。
Object username = request.getSession().getAttribute("username");
if (username != null && !username.equals("")) {
return true;
}
// 3.如果用户session中不存在,则跳转到登录页面。
response.sendRedirect("/loginPage");
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 {
}
}
这里因为只是做登录认证,所以只用到了 preHandle 方法,逻辑还是比较清晰的,注释标注的也很明白。
5、拦截器配置:springmvc.xml
<!-- 配置全局mapping的拦截器 -->
<mvc:interceptors>
<!-- 如果有多个拦截器,则按照顺序进行配置 -->
<mvc:interceptor>
<!-- /**表示所有URL和子URL路径 -->
<mvc:mapping path="/**"/>
<bean class="top.alanshelby.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
这样,一个简单的拦截器就完成了,还是很简单的吧。
四、小结
拦截器是日常开发中不可或缺的一种应用,这里最常用的实现拦截器的方式就是实现 HandlerInterceptor 接口,自己一定要手动实现一下简单的 demo,让自己真正了解拦截器的使用