一、拦截器入门(工程springmvc-mybatis10
)
1.1 拦截器的定义
HandlerInterceptor1.java
package cn.itcast.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
//测试拦截器1
public class HandlerInterceptor1 implements HandlerInterceptor{
//在进入handler方法之前执行,用于身份认证、身份授权等权限管理
//比如身份认证,如果认证没有通过,则需要此方法拦截,不再向下执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("第一个拦截器----执行前");
//返回false表示拦截,不向下执行
return false;
}
//在进入handler方法之后同时在返回modelAndView之前
//应用场景从modelAndView出发,将一些公用的模型数据在这里传递到视图中(比如菜单的导航)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("第一个拦截器----执行中");
}
//在执行handler方法之后执行
//应用场景:可以使用统一的异常处理,还可以用于统一的日志处理
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("第一个拦截器----执行后");
}
}
说明:拦截器需要实现HandlerInterceptor
接口,同时覆写其中的三个方法。
1.2 拦截器的配置
- 方式一:
springmvc
的拦截器是针对HandlerMapping
进行拦截设置的,如果在某个HandlerMapping
中配置拦截器,经过该HandlerMapping
映射成功的Handler
最终才使用该拦截器。针对某个mapping
配置拦截器:
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
不过这种方式比较麻烦,一般不推荐。
- 方式二:
定义好拦截器之后我们还需要将其配置到springmvc
中,在springmvc.xml
中:
<!--拦截器 -->
<mvc:interceptors>
<!--多个拦截器,顺序执行 -->
<mvc:interceptor>
<mvc:mapping path="/**" /><!-- 表示拦截所有的url,包括子url路径 -->
<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
1.3 测试
这里我们再定义一个拦截器:
HandlerInterceptor2.java
package cn.itcast.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
//测试拦截器2
public class HandlerInterceptor2 implements HandlerInterceptor{
//在进入handler方法之前执行,用于身份认证、身份授权等权限管理
//比如身份认证,如果认证没有通过,则需要此方法拦截,不再向下执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("第二个拦截器----执行前");
//返回false表示拦截,不向下执行
return false;
}
//在进入handler方法之后同时在返回modelAndView之前
//应用场景从modelAndView出发,将一些公用的模型数据在这里传递到视图中(比如菜单的导航)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("第二个拦截器----执行中");
}
//在执行handler方法之后执行
//应用场景:可以使用统一的异常处理,还可以用于统一的日志处理
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("第二个拦截器----执行后");
}
}
说明:
- 1、可以看到这里我们两个拦截器中都不放行(
preHandle
方法返回false
)测试结果为:
第一个拦截器----执行前
第二个拦截器----执行前
第二个拦截器----执行中
第一个拦截器----执行中
第二个拦截器----执行后
第一个拦截器----执行后
结论:当都放行时方法preHandle
按顺序执行,postHandle
和afterCompletion
逆向执行。
- 2、拦截器1放行,拦截器2不放行(将拦截器2的
preHandle
方法返回false
),测试结果为:
第一个拦截器----执行前
第二个拦截器----执行前
第一个拦截器----执行后
结论:拦截器1放行,拦截器2的preHandle
方法才会执行。拦截器2的preHandle
方法不放行,则拦截器2的另外两个方法不会执行。只要有一个拦截器不放行,则postHandle
方法都不会执行。
- 3、两个拦截器都不放行,即都返回
false
。测试结果为:
第一个拦截器----执行前
结论:拦截器1的preHandle
不放行,其余方法和其他拦截器中的方法都不会执行。
1.4 小结
根据测试结果,对拦截器进行应用,比如一个统一的日志处理拦截器,则需要将其放在拦截器链中的第一个位置,并且方法preHandle
一定要放行。
比如一个登录认证的拦截器,需要将其放在拦截器链中的第一个位置。但是一般在日志拦截器后面。再比如一个权限校验的拦截器,应该放在登录认证拦截器之后。
二、拦截器的应用(工程springmvc-mybatis11
)
2.1 需求
用户请求url
,拦截器进行拦截校验,如果请求的url
是不需要登录就可以访问的url
(公开地址),则放行。如果请求的url
是需要登录才可以访问的url
,用户session
不存在,则跳转到的登录页面,否则放行。
2.2 Controller方法
LoginController.java
package cn.itcast.ssm.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
//登录
@RequestMapping("/login")
public String login(HttpSession session, String username, String password) throws Exception{
//进行身份认证
session.setAttribute("username", username);//在session中保存用户身份信息
//重定向商品列表页面
return "redirect:/items/queryItems.action";
}
//退出
@RequestMapping("/logout")
public String logout(HttpSession session) throws Exception{
//清除session
session.invalidate();
return "redirect:/items/queryItems.action";
}
}
2.3 登录认证拦截实现
LoginInterceptor.java
package cn.itcast.ssm.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;
//登录认证拦截器
public class LoginInterceptor implements HandlerInterceptor{
//在进入handler方法之前执行,用于身份认证、身份授权等权限管理
//比如身份认证,如果认证没有通过,则需要此方法拦截,不再向下执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
//获取请求的url
String url = request.getRequestURI();
//判断url是否是公开地址,实际使用时应该将公开地址配置在配置文件中,这里公开地址是登录提交的地址
if(url.indexOf("login.action") >= 0){
//如果是登录提交,则放行
return true;
}
//判断session
HttpSession session = request.getSession();
//从session中取出用于身份信息
String username = (String) session.getAttribute("username");
if(username != null){
//身份信息存在,放行
return true;
}
//没有校验通过,表示用户身份需要认证,此时需要跳转到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
//返回false表示拦截,不向下执行
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 {
}
}
2.4 拦截器配置
和之前一样:
<mvc:interceptor>
<mvc:mapping path="/**" /><!-- 登录认证拦截器 -->
<bean class="cn.itcast.ssm.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
2.5 测试
在测试之前我们可以在页面itemsList.jsp
中加上一些信息:
当前用户:${username },
<c:if test="${username != null}">
<a href="${pageContext.request.contextPath }/logout.action">退出</a>
</c:if>
而登录页面为jsp/login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isELIgnored="false"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>系统登录</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<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>