SpringMVC拦截器介绍
什么是拦截器
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
拦截器快速入门
1.创建拦截器实现HandlerInterceptor接口
2.配置拦截器
3.测试拦截器的拦截效果
1.创建拦截器实现HandlerInterceptor接口
package com.pjh.HandleInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
/*目标方法执行前执行*/
/*false代表不放行,true代表放行*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("第一个:preHandle");
return false;
}
/*目标方法执行之后,视图返回之前执行*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("第一个:postHandle");
}
/*全部流程执行完毕后执行*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("第一个:afterCompletion");
}
}
方法介绍
- preHandle() 方法:该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行;
当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。
- postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。
- afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。
2.配置拦截器
开发拦截器就像开发servlet或者filter一样,都需要在配置文件进行配置,配置代码如下:
上面的代码中,
注意:
拦截器的执行流程
单个拦截器
在运行程序时,拦截器的执行是有一定顺序的,该顺序与配置文件中所定义的拦截器的顺序相关。
单个拦截器,在程序中的执行流程如下图所示:
1.程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行。
2.在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应。
3.在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。
测试案例
定义一个拦截器,访问资源后观察控制台输出
package com.pjh.HandleInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
/*目标方法执行前执行*/
/*false代表不放行,true代表放行*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("第一个:preHandle");
return false;
}
/*目标方法执行之后,视图返回之前执行*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("第一个:postHandle");
}
/*全部流程执行完毕后执行*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("第一个:afterCompletion");
}
}
配置多个拦截器的执行流程
多个拦截器(假设有两个拦截器Interceptor1和Interceptor2,并且在配置文件中, Interceptor1拦截器配置在前),在程序中的执行流程如下图所示:
从图可以看出,当有多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。
测试案例
mvc配置文件中的数据
第一个拦截器中的代码
/*目标方法执行前执行*/
/*false代表不放行,true代表放行*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("第一个:preHandle");
return true;
}
/*目标方法执行之后,视图返回之前执行*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("第一个:postHandle");
}
/*全部流程执行完毕后执行*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("第一个:afterCompletion");
}
第二个拦截器中的代码
package com.pjh.HandleInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor2 implements HandlerInterceptor {
/*目标方法执行前执行*/
/*false代表不放行,true代表放行*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("第二个:preHandle");
return true;
}
/*目标方法执行之后,视图返回之前执行*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("第二个:postHandle");
}
/*全部流程执行完毕后执行*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("第二个:afterCompletion");
}
}
访问资源后的控制台输出
拦截器案例1
拦截器通过判断get方法中的参数来判断是否跳转到页面的案例
拦截器代码配置
package com.pjh.HandleInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
/*目标方法执行前执行*/
/*false代表不放行,true代表放行*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("第一个:preHandle");
String param = request.getParameter("param");
/*如果参数为false返回success页面*/
/*如果参数不为false返回error页面*/
if ("yes".equals(param)){
return true;
}else {
request.getRequestDispatcher("error.jsp").forward(request,response);
return false;
}
}
/*目标方法执行之后,视图返回之前执行*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("第一个:postHandle");
}
/*全部流程执行完毕后执行*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("第一个:afterCompletion");
}
}
当get方法所带参数为yes时
当get方法所带参数不为yes时
拦截器案例2判断用户有没有登入
项目背景:
以访问一个后台管理系统为例,如果用户登入了则让其可以访问后台管理系统,如果用户没有登入则在用户点击任意菜单时都跳转到登入页面
点击侧边栏的任何一个按钮均跳转到登入页面
拦截器的配置
拦截器代码实现
package com.pjh.Interceptor;
import com.pjh.domain.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.print.DocFlavor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class PrivilegeInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/*如果session域中的user对象为空则跳转到登入页面说明用户没有登入或则登入失败*/
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
System.out.println("拦截器:"+user);
if (user==null){
response.sendRedirect(request.getContextPath()+"/login.jsp");
return false;
}
/*用户存在放行*/
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
Controller层/login代码
调用Service层函数判断是否存在该用户
@Autowired
private UserService userService;
@RequestMapping("/login")
public String login(String username, String password, HttpSession session){
System.out.println(username);
System.out.println(password);
User user =userService.login(username,password);
if (user!=null){
session.setAttribute("user",user);
System.out.println(user);
return "redirect:/index.jsp";
}else {
return "redirect:/login.jsp";
}
}
Service层login函数代码
public User login(String username, String password) {
User user=userDao.findPasswordAndUsername(username,password);
return user;
}
Dao层查询用户是否存在代码
注意这里要捕获异常否则当查询到用户为空的时候会抛出异常
public User findPasswordAndUsername(String username, String password) {
try{
User user = jdbcTemplate.queryForObject("select * from sys_user where username=? and password=?", new BeanPropertyRowMapper(User.class), username, password);
System.out.println(user);
return user;
}catch (EmptyResultDataAccessException e){
return null;
}
}
经过以上设置只有用户输入正确的用户名及其密码之后才可以进入后台管理页面
过滤器的介绍及其使用
啥是过滤器
顾名思义即过滤掉一些东西,比如我们经历的高考中考都是过滤器,他过滤掉一些在学习这一方面不是很好的人,而那些成绩好的人则升入高中,大学。
但是java中的过滤器与生活中的过滤器的作用是相差无几的,即按照制定的一些规则来控制一些对象
Filer的作用:
过滤器是出于客户端与服务器端之间的一道过滤网,在访问资源之前,通过一系列的过滤器对请求进行修改,判断等。把不符合规则的请求在中途拦截或修改,拦截或修改响应
应用场景
自动登录
统一设置编码格式
访问权限控制
敏感字符过滤等
过滤器快速入门
需要导入的jar坐标
org.apache.tomcat
servlet-api
6.0.29
org.mortbay.jetty
servlet-api-2.5
6.1.7
1. 步骤:
- 定义一个类,实现接口Filter
- 复写方法
3.1注解配置
3.2web.xml配置
3.1注解配置
Filter类代码如下
package com.pjh;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/*在执行所有资源之前都会执行该过滤器*/
@WebFilter("/*")
public class MyFilter1 implements Filter {
/*在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源*/
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}
/*:每一次请求被拦截资源时,会执行。执行多次*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行过滤器操作");
/*放行操作*/
filterChain.doFilter(servletRequest, servletResponse);
}
/*在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源*/
public void destroy() {
System.out.println("过滤器销毁操作");
}
}
3.2web.xml配置
MyFilter1
com.pjh.MyFilter1
MyFilter1
/*
1. 过滤器执行流程
- 执行过滤器
- 执行放行后的资源
- 回来执行过滤器放行代码下边的代码
2. 过滤器生命周期方法
1. init:
在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用 于加载资源
**
2. doFilter:
每一次请求被拦截资源时,会执行。执行多次
3. destroy:
在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执 行destroy方法。只执行一次。用于释放资源
4. 过滤器配置详解
* 拦截路径配置:
** 1. 具体资源路径:**
/index.jsp 只有访问index.jsp资源时,过滤器才会被执行
2. 拦截目录:
/user/* 访问/user下的所有资源时,过滤器都会被执行
3. 后缀名拦截:
*.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
4. 拦截所有资源:
/* 访问所有资源时,过滤器都会被执行
* 拦截方式配置:资源被访问的方式
*** 注解配置:**
* 设置dispatcherTypes属性
1. REQUEST:默认值。浏览器直接请求资源
2. FORWARD:转发访问资源
3. INCLUDE:包含访问资源
4. ERROR:错误跳转资源
5. ASYNC:异步访问资源
例
package com.pjh;
import javax.print.DocFlavor;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/*在执行所有资源之前,而且是直接访问该资源的时候才会执行该过滤器*/
@WebFilter(value = "/*",dispatcherTypes =DispatcherType.REQUEST )
public class MyFilter1 implements Filter {
/*在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源*/
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}
/*:每一次请求被拦截资源时,会执行。执行多次*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行过滤器操作");
/*放行操作*/
filterChain.doFilter(servletRequest, servletResponse);
}
/*在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源*/
public void destroy() {
System.out.println("过滤器销毁操作");
}
}
* web.xml配置
* 设置 标签即可
例
MyFilter1
com.pjh.MyFilter1
MyFilter1
/*
REQUEST
5. 过滤器链(配置多个过滤器)
* 执行顺序:如果有两个过滤器:过滤器1和过滤器2
看图就行,很好理解
1. 过滤器1
2. 过滤器2
3. 资源执行
4. 过滤器2
5. 过滤器1
* 过滤器先后顺序问题:
- 注解配置:按照类名的字符串比较规则比较,值小的先执行
如: AFilter 和 BFilter,AFilter就先执行了
- web.xml配置:
谁定义在上边,谁先执行
案例一:过滤器之-用户登入案例
项目背景:
以访问一个后台管理系统为例,如果用户登入了则让其可以访问后台管理系统,如果用户没有登入则不能访问任何的该网站页面,并且自动跳转到登入页面,在登入后才可以访问其他页面
点击侧边栏的任何一个按钮均跳转到登入页面
过滤器代码实现
package com.pjh.Filter;
import com.pjh.domain.User;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class LoginFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤启动");
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器执行");
System.out.println(servletRequest);
/*强制转换*/
HttpServletRequest request= (HttpServletRequest)servletRequest;
/*获取请求资源的路径*/
String uri = request.getRequestURI();
/*判断是否是包含登入资源的相关路径要注意排除掉 css/js/图片/验证码等资源*/
if (uri.contains("/login") ||uri.contains("/login.jsp")|| uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("/checkCodeServlet") ||uri.contains("/img/")){
/*是访问登入相关的路径,放行*/
filterChain.doFilter(servletRequest,servletResponse);
}else{
/*不是登入的相关路径,看看用户有没有登入*/
HttpSession session = request.getSession();
/*从session域中获取user,看看有没有登入*/
User user = (User)session.getAttribute("user");
System.out.println(user);
if (user==null){
/*未登入跳转到登入页面*/
request.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
}else{
/*已经登入放行*/
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
public void destroy() {
System.out.println("过滤器销毁");
}
}
在web.xml中对过滤器的配置
filter1
com.pjh.Filter.LoginFilter
filter1
/*
经过以上配置用户只有在登入之后才可以对资源进行访问了
案例二 过滤器之敏感词汇的过滤
应用场景:
比如在王者荣耀当中经常有低素质的人在里面狂喷别人,满嘴污秽词汇,这个时候为了营造良好的游戏氛围,就要使用到敏感词汇过滤的技术,将骂人的词汇变成“****”
基本原理:
这时候我们就要使用过滤器了,在过滤器中对这些敏感词汇进行等一系列操作
下面通过一张图来讲解
**
比如我们的敏感词汇为“坏蛋”,如果我们输入“你是坏蛋”那么过滤后的内容就为“你是**”,在过滤器中由于reqest对象没有setParemeter操作,所以我们只能对request对象的getParameter方法进行增强,并且产生一个新的Parameter对象,并在新的request对象中加入过滤后的词汇,供其他方法调用
Filter中是使用动态代理的方式来对敏感词汇进行过滤的
需求对以下敏感词汇进行过滤,并将敏感词汇替换为“”*
笨蛋
傻瓜
分析:
1.对request对象进行增强,增强获取参数的相关方法
2.放行,传递代理对象
Filter函数代码如下
package com.pjh.Filter;
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
import javax.servlet.*;
import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public class SensitiveWordFilter implements Filter {
/*创建敏感词汇集合*/
private List list = new ArrayList();
public void init(FilterConfig filterConfig) throws ServletException {
/*读取相关的敏感词汇文件并存入对应的集合中*/
try{
/*获取文件的真实路径*/
ServletContext servletContext = filterConfig.getServletContext();
InputStream path = this.getClass().getResourceAsStream("/SensitiveWord.txt");
/*读取文件*/
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(path,"UTF-8"));
/*将文件的每一行数据添加到list集合中*/
String line=null;
while ((line=bufferedReader.readLine())!=null){
list.add(line);
}
bufferedReader.close();
// System.out.println(list);
}catch (Exception e){
System.out.println(e);
}
}
public void doFilter(final ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter1:"+servletRequest);
ServletRequest proxy_req= (ServletRequest) Proxy.newProxyInstance(servletRequest.getClass().getClassLoader(), servletRequest.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*1.判断是否是getParameter方法
2. 增强getParameter方法 */
if (method.getName().equals("getParameter")){
/*增强返回值*/
/*获取返回值*/
String value= (String)method.invoke(servletRequest,args);
/*遍历集合判断字符串中是否存在敏感词汇*/
if (value!=null){
for (String s : list) {
if (value.contains(value)){
value=value.replace(s,"***");
}
}
}
return value;
}
return method.invoke(servletRequest,args);
}
});
System.out.println("list集合数据:"+list);
System.out.println("filter2proxy_req:"+proxy_req);
/*放行*/
filterChain.doFilter(proxy_req,servletResponse);
}
public void destroy() {
System.out.println("销毁方法");
}
}
在web.xml中设置拦截路径
filter2
com.pjh.Filter.SensitiveWordFilter
filter2
/filter/*
Controller类代码
package com.pjh.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.UnsupportedEncodingException;
/*设置编码格式*/
@Controller
@RequestMapping(value = "/filter",produces = "text/html;charset=utf-8")
public class TestFilterController {
@RequestMapping("/test1")
@ResponseBody
public String test1(ServletRequest request, ServletResponse response) throws UnsupportedEncodingException {
System.out.println("test1:"+request);
String id = request.getParameter("id");
System.out.println("test1ID:"+id);
return "你的id是"+id;
}
}
未设置过滤器前
设置过滤器后
过滤器与拦截器的区别以及过滤器的基本:
过滤器与拦截器的区别
1.过滤器:
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等
2.拦截器:
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理
3.过滤器和拦截器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
过滤器
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
System.out.println("before...");
chain.doFilter(request, response);
System.out.println("after...");
}
chain.doFilter(request, response);这个方法的调用作为分水岭。事实上调用Servlet的doService()方法是在chain.doFilter(request, response);这个方法中进行的。