Spring MVC 拦截器
本小记学习目标
-
拦截器的认识
-
拦截器的配置
-
拦截器的执行流程介绍
一、拦截器的认识
Spring MVC的拦截器Interceptor与Servlet中的过滤器Filter类似,它的主要作用就是用于拦截用户的请求并做出相应的处理(验证权限、记录请求日志、判断用户否登录……)。
在Spring MVC中使用拦截器需要对拦截器进行定义和配置。
定义:
1. 通过实现HandlerInterceptor接口或者继承HandlerInterceptor接口的实现类
2. 通过实现WebRequestInterceptor接口或者继承WebRequestInterceptor接口的实现类
关于HandlerInterceptor接口中定义了三个方法
preHandle:这个方法在控制器处理请求之前执行,它的返回值是一个boolean值,返回true则继续后续的操作,返回false则中断后续操作
postHandle:这个方法在控制器处理请求之后,但在解析视图之前执行,在这个方法中可以对请求域中的模型和视图做进一步的修改
afterCompletion:这个方法在控制器处理请求方法执行完成之后执行,也就是在视图渲染结束后,通常用来实现一些资源的清理、记录日志等工作
配置:
为了让拦截器生效需要在Spring MVC的配置文件中进行配置
<!-- 拦截器配置 -->
<mvc:conterceptors>
<!-- 全局拦截器配置 -->
<bean class="com.xiaoie.interceptor.GlobalInterceptor"/>
<mvc:interceptor>
<!-- 配置拦截器的作用路径 -->
<mvc:mapping path="/**"/>
<!-- 配置不需要拦截的路径 -->
<mvc:exclude-mapping path=""/>
<bean class="com.xiaoxie.interceptor.Interceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- 配置拦截器的作用路径 -->
<mvc:mapping path="/test"/>
<bean class="com.xiaoxie.interceptor.Interceptor2"/>
</mvc:interceptor>
</mvc:conterceptors>
<mvc:interceptors>元素用来配置一组拦截器,它的子元素<bean>定义的是全局拦截器,所有请求都会拦截。<mvc:interceptor>则定义了指定路径的拦截器,其子元素<mvc:mapping>用来配置拦截器作用的路径,路径在它的属性path中定义,上面的/**表示拦截所有路径,/test表示拦截所有以/test结尾的路径。如果需要排除不需要拦截的路径需要在<mvc:exclude-mapping>子元素进行配置
注意:<mvc:interceptor>下的子元素需要按顺序进行配置
二、拦截器的执行流程介绍
假设在配置文件中只定义了一个拦截器,程序执行概要过程如下:
新增一个Maven的war工程
在pom.xml中添加如下依赖
<
dependencies
>
<!-- context -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-context
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- Spring MVC -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-
webmvc
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- Spring web -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-web
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- commons-logging -->
<
dependency
>
<
groupId
>commons-logging
</
groupId
>
<
artifactId
>commons-logging
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
<!-- spring-
aop
-->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-
aop
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<
dependency
>
<
groupId
>javax.servlet
</
groupId
>
<
artifactId
>
jstl
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
</
dependencies>
新增一controller的实现类 com.xiaoxie.controller.InterceptorController
package com.xiaoxie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public
class InterceptorController {
//定义一个logger,用来记录日志
private
static
final Log
logger = LogFactory.
getLog(InterceptorController.
class);
@RequestMapping(
"/test")
public String test(Model
model) {
logger.info(
"============处理用户请求============");
model.addAttribute(
"message",
"OK");
return
"test";
}
}
添加拦截器的实现类com.xiaoxie.interceptor.TestInterceptor
package com.xiaoxie.interceptor;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.xiaoxie.controller.InterceptorController;
public
class TestInterceptor
implements HandlerInterceptor{
//定义一个logger,用来记录日志
private
static
final Log
logger = LogFactory.
getLog(InterceptorController.
class);
//在HandlerInterInterceptor的接口中有三个
defalut
方法我们需要重写一下以做测试
@Override
public
boolean preHandle(HttpServletRequest
request, HttpServletResponse
response, Object
handler)
throws Exception {
logger.info(
"==========在处理请求前执行方法:preHandle=========");
logger.info(
"~~~~~~~~~~~~~~~检查用户权限~~~~~~~~~~~~~~~");
//这里返回true则继续,否则会中断后续操作
return
true;
}
@Override
public
void postHandle(HttpServletRequest
request, HttpServletResponse
response, Object
handler,
ModelAndView
modelAndView)
throws Exception {
Map<String, Object>
model =
modelAndView.getModel();
Set<Entry<String, Object>>
entrySet =
model.entrySet();
logger.info(
"==========在处请求后视图返回前执行:postHandle=========");
for (Map.Entry<String, Object>
entry :
entrySet) {
logger.info(
entry.getKey() +
"====>" +
entry.getValue());
}
modelAndView.addObject(
"message",
"OKOK!");
}
@Override
public
void afterCompletion(HttpServletRequest
request, HttpServletResponse
response, Object
handler, Exception
ex)
throws Exception {
logger.info(
"==========在视图渲染结束之后执行:afterCompletion=========");
}
}
添加springmvc的配置文件springmvc.xml,在其中添加的内容如下
<!-- spring自动扫描的包定义 -->
<
context:component-scan
base-package=
"com.xiaoxie.controller"
/>
<!-- 配置视图解析器 -->
<
bean
class=
"org.springframework.web.servlet.view.InternalResourceViewResolver"
id=
"internalResourceViewResolver"
>
<!-- 前缀 -->
<
property
name=
"
prefix"
value=
"/WEB-INF/jsp/"
/>
<!-- 后缀 -->
<
property
name=
"
suffix"
value=
".jsp"
/>
</
bean
>
<!-- 定义一个全局拦截器 -->
<
mvc:interceptors
>
<
bean
class=
"com.xiaoxie.interceptor.TestInterceptor"
></
bean
>
</
mvc:interceptors
>
在web.xml中新增对前端控制器的配置
<!-- 前端控制器 -->
<
servlet
>
<
servlet-name
>springDispatcherServlet
</
servlet-name
>
<
servlet-class
>org.springframework.web.servlet.DispatcherServlet
</
servlet-class
>
<
init-param
>
<
param-name
>contextConfigLocation
</
param-name
>
<
param-value
>classpath:springmvc.xml
</
param-value
>
</
init-param
>
<
load-on-startup
>1
</
load-on-startup
>
</
servlet
>
<!-- Map all requests to the DispatcherServlet for handling -->
<
servlet-mapping
>
<
servlet-name
>springDispatcherServlet
</
servlet-name
>
<
url-pattern
>/
</
url-pattern
>
</
servlet-mapping
>
<!-- 配置解决
psot
请求中文乱码问题 -->
<
filter
>
<
filter-name
>CharacterEncodingFilter
</
filter-name
>
<
filter-class
>org.springframework.web.filter.CharacterEncodingFilter
</
filter-class
>
<
init-param
>
<
param-name
>encoding
</
param-name
>
<
param-value
>
utf-8
</
param-value
>
</
init-param
>
<
init-param
>
<
param-name
>forceEncoding
</
param-name
>
<
param-value
>true
</
param-value
>
</
init-param
>
</
filter
>
<
filter-mapping
>
<
filter-name
>CharacterEncodingFilter
</
filter-name
>
<
url-pattern
>/*
</
url-pattern
>
</
filter-mapping
>
定义一个jsp页面来展示视图/WEB-INF/jsp/test.jsp,在这个页面中直接展示model绑定的内容
${message }
访问工程的/test请求可以看到页面test.jsp展示的内容为OKOK!
在控制台我们可以看到打印的信息如下:
信息: ==========在处理请求前执行方法:preHandle=========
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController preHandle
信息: ~~~~~~~~~~~~~~~检查用户权限~~~~~~~~~~~~~~~
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController test
信息: ============处理用户请求============
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController postHandle
信息: ==========在处请求后视图返回前执行:postHandle=========
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController postHandle
信息: message====>OK
12月 29, 2020 9:25:53 下午 com.xiaoxie.controller.InterceptorController afterCompletion
信息: ==========在视图渲染结束之后执行:afterCompletion=========
以上为单个拦截器的执行流程,当存在多个拦截器的时候(在实际工程中常见),它们的执行是如何的呢?
preHandle方法是按照配置文件中的配置顺序进行执行的,postHandle方法和afterCompletion则是按照配置的相反顺序执行的
模拟用户登录权限验证实例(实际工程中一般也是用拦截器来做用户权限的验证操作)
操作过程:
1.用户通过浏览器 请求登录,携带用户名、密码请求参数
2.如果验证用户名、密码成功则跳转到main.jsp页面
3.如果验证用户名、密码不成功则转发到login.jsp
4.当用户点击主页面中的“退出”链接时回到主页面
新增一个实体类User,com.xiaoxie.pojo.User
package com.xiaoxie.pojo;
public
class User {
private String
uname;
private String
upwd;
public String getUname() {
return
uname;
}
public
void setUname(String
uname) {
this.
uname =
uname;
}
public String getUpwd() {
return
upwd;
}
public
void setUpwd(String
upwd) {
this.
upwd =
upwd;
}
}
新增UserController实现类,com.xiaoie.controller.UserController
package com.xiaoxie.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.xiaoxie.pojo.User;
@
Controller
public
class UserController {
private
static
final Log
logger = LogFactory.
getLog(UserController.
class);
private
static
final String
MAIN =
"main";
private
static
final String
LOGIN =
"login";
@RequestMapping(
"/tologin")
public String toLogin(){
return
"login";
}
@RequestMapping(
"/login")
public String login(User
user,Model
model,HttpSession
session) {
logger.debug(
"请求信息:uname=" +
user.getUname() +
",upwd=" +
user.getUpwd() );
//对用户进行判断
logger.debug(
"对用户信息进行判断");
if(
"xiaoxie".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.invalidate();
return
LOGIN;
}
}
新增一个拦截器实现类,com.xiaoxie.interceptor.LoginInterceptor,在这个方法中只对preHandle接口默认方法进行重写
package com.xiaoxie.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
public
class LoginInterceptor
implements HandlerInterceptor{
@Override
public
boolean preHandle(HttpServletRequest
request, HttpServletResponse
response, Object
handler)
throws Exception {
//获取请求的URI
String
requestURI =
request.getRequestURI();
//login.jsp或/
tologin
不需要处理
if(
requestURI.indexOf(
"/tologin")>=0 ||
requestURI.indexOf(
"/login")>=0) {
return
true;
}
HttpSession
session =
request.getSession();
Object
user =
session.getAttribute(
"user");
if(
user !=
null)
return
true;
//没有登录或且不是登录页面,转发到登录页面去
request.setAttribute(
"msg",
"还没有登录,请先登录!");
request.getRequestDispatcher(
"/WEB-INF/jsp/login.jsp").forward(
request,
response);
return
false;
}
}
在Spring mvc的配置文件中配置这个拦截器
<!-- 定义一个全局拦截器 -->
<
mvc:interceptors
>
<
bean
class=
"com.xiaoxie.interceptor.LoginInterceptor"
></
bean
>
</
mvc:interceptors
>
新增视图index.jsp(在WebContent目录下),login.jsp(/WEB-INF/jsp目录下),main.jsp(/WEB-INF/jsp目录下)
index.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
>Insert title here
</
title
>
</
head
>
<
body
>
<
a
href=
"${pageContext.request.contextPath }
/tologin"
>登录
</
a
>
</
body
>
</
html
>
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
>Insert title here
</
title
>
</
head
>
<
body
>
${msg }
<
form
action=
"${pageContext.request.contextPath }
/login"
method=
"post"
>
用户名:
<
input
type=
"text"
name=
"uname"
/><
br
/>
密 码:
<
input
type=
"password"
name=
"upwd"
/><
br
/>
<
input
type=
"submit"
value=
"登录"
>
</
form
>
</
body
>
</
html
>
main.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
>Insert title here
</
title
>
</
head
>
<
body
>
欢迎用户:${user.uname }
<
a
href=
"${pageContext.request.contextPath }
/logout"
>注销
</
a
>
</
body
>
</
html
>
如上则完成了基本的用户权限校验。