2024.10月11日--- SpringMVC拦截器

拦截器

1 回顾过滤器:

Servlet规范中的三大接口:==Servlet接口,Filter接口、Listener接口。==

过滤器接口,是Servlet2.3版本以来,定义的一种小型的,可插拔的Web组件,可以用来拦截和处理Servlet容器的请求和响应过程。以便查看,提取或以某种方式操作正在客户端与服务器端之间交换的数据。

过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截。Filter 接口中定义了三个方法。

  • init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。「注意」:这个方法必须执行成功,否则过滤器会不起作用。

  • doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。

  • destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次。

2 拦截器的简介

SpringMVC里的拦截器是面向切面编程AOP的一个具体实现,用于对请求做预处理。

1)什么是拦截器:

在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略

2)为什么需要拦截器:

在做身份认证或者是进行日志的记录时,我们需要通过拦截器达到我们的目的。最常用的登录拦截、或是权限校验、或是防重复提交、或是根据业务像12306去校验购票时间,总之可以去做很多的事情

3 拦截器的应用

步骤1):自定义一个类,实现HandlerInterceptor接口,或者继承HandlerInterceptorAdapter抽象类

步骤2):根据自己的需求,重写方法

复制代码
方法1:boolean preHandle()
      - 想要在执行Controller之前执行拦截,就重写该方法。
      - 存在多个interceptor时,它们基于链式方式调用,按照注册的先后顺序依次执行。
      - 方法返回true时,后续有拦截器,就继续执行拦截器,没有就执行controller.
      - 方法返回false时,后续任何内容都不执行了,直接返回浏览器
​
​
方法2:void postHandle():
        会在Controller执行后,视图渲染之前调用该方法。因此可以在这个阶段,对将要返回给客户端的ModelAndView进行处理。
​
方法3:void afterCompletion:
        - 该方法会在视图渲染后被调用,主要是用来进行资源清理工作。
        - 多个拦截器时,依旧是先执行先注册的拦截器的afterCompletion方法
        - 不管处理器是否抛出异常,该方法都将执行。

步骤3)在spring的配置文件中配置拦截器(或者配置类中也可以)

4 拦截器执行流程

4.1 拦截器的执行时机

4.2 拦截器和过滤器一起应用时的执行时机

5 拦截器的应用场景

日志记录:记录请求信息的日志,以便进行信息监控、信息统计等;

权限检查:如登录校验,在处理器处理之前先判断是否已经登录;

性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。

通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用。还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的都可以用拦截器来实现。

6 当前路径绑定权限拦截器

package com.ssm.netctoss.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* 步骤1):自定义一个类,实现HandlerInterceptor接口,或者继承HandlerInterceptorAdapter抽象类

* 步骤2):根据自己的需求,重写方法

* 步骤3)在spring的配置文件中配置拦截器(或者配置类中也可以)

*/

public class CurrentPathPrivilegeInterceptor implements HandlerInterceptor {

/**

*

* @param request 请求对象

* @param response 响应对象

* @param handler 请求路径对应的Controller里的方法对象(反射机制)

* /fee/findByPage

* public String findByPage(){

* ........

* }

* @return

* @throws Exception

*/

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

System.out.println("----------preHandle------------");

System.out.println("-------注意:需要给每个用户添加0,8,9权限,在数据库中-----------");

System.out.println("-------注意:需要给每个用户添加0,8,9权限,在数据库中-----------");

System.out.println("-------注意:需要给每个用户添加0,8,9权限,在数据库中-----------");

//获取请求路径:

String uri = request.getRequestURI();

int currentPrivilege = -1;

if(uri.contains("/toIndex")){

currentPrivilege = 0;

}else if(uri.contains("/role/")){

currentPrivilege = 1;

}else if(uri.contains("/admin/")){

currentPrivilege = 2;

}else if(uri.contains("/fee/")) {

currentPrivilege = 3;

}else if(uri.contains("/account/")) {

currentPrivilege = 4;

}else if(uri.contains("/service/")) {

currentPrivilege = 5;

}else if(uri.contains("/bill/")) {

currentPrivilege = 6;

}else if(uri.contains("/report/")) {

currentPrivilege = 7;

}else if(uri.contains("/user/show")) {

currentPrivilege = 8;

}else if(uri.contains("/user/toUpdate")) {

currentPrivilege = 9;

}

request.getSession().setAttribute("currentPrivilege", currentPrivilege);

return true;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

System.out.println("----------postHandle------------");

HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

System.out.println("----------afterCompletion------------");

HandlerInterceptor.super.afterCompletion(request, response, handler, ex);

}

}

编写导航高亮提示

复制代码
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
复制代码
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
复制代码
<div id="navi">
复制代码
   <ul id="menu">
复制代码
      <li><a href="../index/toIndex" class="<c:choose><c:when test="${currentPrivilege==0}">index_on</c:when><c:otherwise>index_off</c:otherwise></c:choose>"></a></li>
复制代码
      <li><a href="../role/findByPage" class="<c:choose><c:when test="${currentPrivilege==1}">role_on</c:when><c:otherwise>role_off</c:otherwise></c:choose>"></a></li>
复制代码
      <li><a href="../admin/findByPage" class="<c:choose><c:when test="${currentPrivilege==2}">admin_on</c:when><c:otherwise>admin_off</c:otherwise></c:choose>"></a></li>
复制代码
      <li><a href="../fee/findByPage" class="<c:choose><c:when test="${currentPrivilege==3}">fee_on</c:when><c:otherwise>fee_off</c:otherwise></c:choose>"></a></li>
复制代码
      <li><a href="../account/findByPage" class="<c:choose><c:when test="${currentPrivilege==4}">account_on</c:when><c:otherwise>account_off</c:otherwise></c:choose>"></a></li>
复制代码
      <li><a href="../service/searchService" class="<c:choose><c:when test="${currentPrivilege==5}">service_on</c:when><c:otherwise>service_off</c:otherwise></c:choose>"></a></li>
复制代码
      <li><a href="../bill/bill_list.html" class="bill_off"></a></li>
复制代码
      <li><a href="../report/report_list.html" class="report_off"></a></li>
复制代码
      <li><a href="../user/showUserInfo" class="<c:choose><c:when test="${currentPrivilege==8}">information_on</c:when><c:otherwise>information_off</c:otherwise></c:choose>"></a></li>
复制代码
      <li><a href="../user/toUpdatePwd" class="<c:choose><c:when test="${currentPrivilege==9}">password_on</c:when><c:otherwise>password_off</c:otherwise></c:choose>"></a></li>
复制代码
   </ul>
复制代码
</div>

7 登录拦截

package com.ssm.netctoss.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* 使用拦截器,来完成用户是否登录过。 如果没有登录,应该跳转到登录页面,强制其登录

*

* 返回值:

* false: 不执行后续的代码,包括Controller

* true: 执行后续的代码,如果有下一个拦截器,就执行下一个拦截器的preHandle。如果没有拦截器,执行Controller

*/

public class LoginInterceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

System.out.println("----------LoginInterceptor的preHandle-----------");

//获取session,从中获取绑定的信息

Object loginAdmin = request.getSession().getAttribute("LOGINADMIN");

if (loginAdmin == null) {

//没有登录过,就跳转

response.sendRedirect(request.getContextPath()+"/login/toLogin");

return false;

}

return true;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

System.out.println("----------LoginInterceptor的postHandle----------");

HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

System.out.println("----------LoginInterceptor的afterCompletion----------");

HandlerInterceptor.super.afterCompletion(request, response, handler, ex);

}

}

8 权限验证拦截器

package com.ssm.netctoss.interceptor;

import com.ssm.netctoss.pojo.Admin;

import com.ssm.netctoss.pojo.Privilege;

import com.ssm.netctoss.pojo.Role;

import com.ssm.netctoss.service.AdminService;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

/**

* 登录用户的权限校验拦截:

* 比如:登录用户,只能访问管理员,资费,账务,没有其他权限,那么相应的导航栏,有的能访问,有的不能访问

*

*

* admin_info ----> admin_role---->role_info--->role_privilege-->privilege_info

* caocao 管理员1 100 1 2 3

* 营业员 200 3 4 5 6

* 经理 300 7

*/

public class PrivilegeInterceptor extends HandlerInterceptorAdapter {

/**

* 因为拦截器是运行在Spring容器中维护的。(Bean),因此可以使用DI注入其他业务层/控制层的各种类型属性

*/

// @Resource

// private AdminService adminService;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//获取当前登录用户的所有权限

Admin admin = (Admin) request.getSession().getAttribute("LOGINADMIN");

//创建一个Set集合,用于存储该用户的所有权限(privilegeId,name)

Set<Privilege> privileges = new HashSet<Privilege>(); // Privileges实体类要重新hashCode和equals方法

List<Role> roles = admin.getRoles();

for (Role role : roles) {

//添加进集合,并去重

privileges.addAll(role.getPrivileges());

}

/*从当前的请求路径上获取对应的绑定权限*/

int currentPrivilege = (Integer)(request.getSession().getAttribute("currentPrivilege"));

/*查看当前路径的绑定权限是否在当前用户的权限集合里,如果没有,就跳转进行提示*/

for (Privilege privilege : privileges) {

if(privilege.getPrivilegeId()==currentPrivilege){

return true;

}

}

//如果在循序期间,没有遇到return true,说明要访问的路径用户是没有该权限的。因此要做一个跳转

response.sendRedirect(request.getContextPath()+"/login/nopower");

return false;

}

}

注册拦截器

<!--注册拦截器: 拦截器的执行顺序与配置的先后有关系。 先配置的先执行-->

<mvc:interceptors>

<!--配置登录拦截器-->

<mvc:interceptor>

<mvc:mapping path="/**"/>

<mvc:exclude-mapping path="/login/login"/>

<mvc:exclude-mapping path="/login/getCheckCode"/>

<mvc:exclude-mapping path="/login/toLogin"/>

<bean class="com.ssm.netctoss.interceptor.LoginInterceptor"></bean>

</mvc:interceptor>

<!-- 当前路径的权限绑定拦截器 -->

<mvc:interceptor>

<!-- 需要拦截的各种路径:-->

<mvc:mapping path="/**"/>

<!-- <mvc:mapping path="/login/toIndex"/>-->

<!-- <mvc:mapping path="/login/showUserInfo"/>-->

<!-- 不需要拦截的路径:-->

<mvc:exclude-mapping path="/login/toLogin"/>

<mvc:exclude-mapping path="/login/login"/>

<mvc:exclude-mapping path="/login/logout"/>

<!-- 手动配置拦截器的Bean对象 -->

<bean class="com.ssm.netctoss.interceptor.CurrentPathPrivilegeInterceptor"></bean>

</mvc:interceptor>

<mvc:interceptor>

<mvc:mapping path="/**"/>

<mvc:exclude-mapping path="/login/*"/>

<bean class="com.ssm.netctoss.interceptor.PrivilegeInterceptor"></bean>

</mvc:interceptor>

</mvc:interceptors>

9 拦截器与过滤器的比较

**①** 拦截器是基于java的反射机制的,而过滤器是基于函数回调。

**②** 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。

**③** 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

**④** 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

**⑤** 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

**⑥** 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

**⑦ **过滤器和拦截器触发时机、时间、地方不一样

**⑧**过滤器包裹住servlet,servlet包裹住拦截器。

相关推荐
星河梦瑾44 分钟前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富1 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想1 小时前
JMeter 使用详解
java·jmeter
言、雲1 小时前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
TT哇1 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
Yvemil71 小时前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。1 小时前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
.生产的驴2 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文2 小时前
宠物管理系统:Dao层
java·开发语言·宠物
王ASC2 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web