目录
1、拦截器介绍
Spring MVC 拦截器(Interceptor)是在请求处理的过程中,能够拦截并干预请求的一种机制。拦截器通常用于在请求到达控制器之前或之后执行一些额外的逻辑操作,比如权限检查、日志记录、用户身份验证等。拦截器是 Spring MVC 框架提供的一种强大工具,用于实现横切关注点(Cross-cutting Concerns)的逻辑。
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方
法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
以下是一些关键的概念和特点,以及如何在 Spring MVC 中使用拦截器:
- 拦截器的特点:
-
- 拦截器可以在请求处理的不同阶段执行预定义的逻辑,如请求到达控制器之前(preHandle)、控制器执行后视图渲染之前(postHandle)、视图渲染之后(afterCompletion)等。
- 可以定义多个拦截器,并按顺序应用于请求处理流程。
- 拦截器可以用于实现日志、安全检查、性能监控等横切关注点。
- 创建拦截器: 自定义拦截器需要实现 HandlerInterceptor 接口,并实现其中的方法,如:
-
- preHandle(HttpServletRequest request, HttpServletResponse response, Object handler): 在控制器方法执行前调用。
- postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView): 在控制器方法执行后、视图渲染前调用。
- afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex): 在视图渲染后调用,用于清理资源等。
- 注册拦截器: 在 Spring MVC 配置文件(如 spring-mvc.xml)中进行拦截器的配置和注册,使用 <mvc:interceptors> 元素配置拦截器,并指定拦截器类。
- 拦截器执行顺序: 拦截器的执行顺序与它们在配置文件中的声明顺序一致。首先声明的拦截器将在请求处理过程中先被调用。
- 拦截器的应用场景:
-
- 权限验证:检查用户是否具有足够权限访问某个资源。
- 日志记录:记录请求的详细信息,如请求路径、参数、执行时间等。
- 跨域处理:在请求到达控制器之前添加 CORS 头部。
- 缓存控制:在响应中添加缓存相关的头部信息。
2、Interceptor和Filter的区别
拦截器(Interceptor)和过滤器(Filter)是在 Java Web 应用中用于处理请求和响应的两种不同的机制,它们有一些区别和特点:
- 位置不同:
-
- 拦截器是在 Spring MVC 框架内部实现的,用于拦截和干预请求处理流程。拦截器的应用范围仅限于 Spring MVC 控制器方法调用的前后,不会直接参与 Servlet 请求的预处理和后处理。
- 过滤器是 Java Servlet 规范中的一部分,它在请求到达 Servlet 之前进行预处理,或在响应返回客户端之前进行后处理。
- 功能不同:
-
- 拦截器可以更细粒度地控制请求处理流程,比如在请求前后添加逻辑、处理异常、修改模型数据等。拦截器通常用于实现应用层面的逻辑,如权限检查、日志记录、事务管理等。
- 过滤器用于处理请求和响应的内容,如字符编码、请求参数解析、GZIP 压缩等。过滤器更关注于请求和响应的底层处理。
- 使用场景不同:
-
- 拦截器适用于需要在控制器方法执行前后进行一些通用逻辑的场景,例如用户身份验证、权限控制、日志记录等。
- 过滤器适用于需要在请求到达 Servlet 或响应返回客户端之前进行通用处理的场景,例如字符编码、URL 路径转换等。
- 耦合性不同:
-
- 拦截器通常与框架紧密集成,例如 Spring MVC 的拦截器依赖于 Spring 框架。
- 过滤器是 Servlet 规范的一部分,与框架无关,可用于任何 Servlet-based 应用。
- 配置方式不同:
-
- 拦截器通常在 Spring MVC 的配置文件中声明和配置。
- 过滤器在 web.xml 文件中进行配置。
虽然拦截器和过滤器在某些功能上有一些重叠,但它们的设计目标和使用场景不同。在实际应用中,你可以根据需要选择使用拦截器或过滤器,或者在某些情况下结合使用它们来实现不同层次的处理和逻辑。
3、如何使用
自定义拦截器三个步骤:
① 创建拦截器类实现HandlerInterceptor接口
MyInterceptor1:
package com.xzl.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 逐梦苍穹
*/
public class MyInterceptor1 implements HandlerInterceptor {
@Override
//在目标方法执行之前 执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
System.out.println("preHandle ①");
String param = request.getParameter("param");
if ("yes".equals(param)) {
return true;
} else {
request.getRequestDispatcher("/error.jsp").forward(request, response);
return false;//返回true代表放行 返回false代表不放行
}
}
@Override
//在目标方法执行之后 视图对象返回之前执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
modelAndView.addObject("name", "逐梦苍穹");
System.out.println("postHandle ①");
}
@Override
//在流程都执行完毕后 执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("afterCompletion ①");
}
}
MyInterceptor2:
package com.xzl.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyInterceptor2 implements HandlerInterceptor {
@Override
//在目标方法执行之前 执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
System.out.println("preHandle ②");
return true;
}
@Override
//在目标方法执行之后 视图对象返回之前执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("postHandle ②");
}
@Override
//在流程都执行完毕后 执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("afterCompletion ②");
}
}
② 在spring-mvc.xml中配置拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
<!--1、mvc注解驱动-->
<mvc:annotation-driven/>
<!--2、配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--3、静态资源权限开放-->
<mvc:default-servlet-handler/>
<!--4、组件扫描 扫描Controller-->
<context:component-scan base-package="com.xzl.controller"/>
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.xzl.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.xzl.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
拦截器的执行顺序取决于配置文件中的配置顺序
③ 测试拦截器的拦截效果
4、拦截器实现方法
方法名 | 参数 | 功能说明 |
preHandle | HttpServletRequest request, HttpServletResponse response, Object handler | 在请求到达控制器之前调用,用于权限验证等逻辑。 如果返回 true,请求继续流向控制器;否则中断请求。 |
postHandle | HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView | 在控制器方法执行后、视图渲染之前调用, 可以修改模型数据或添加通用数据。 |
afterCompletion | HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex | 在视图渲染后调用,用于清理资源、处理异常等操作。 |
5、工作流程
当同时出现Filter、DispatcherServlet、Interceptor的时候,执行流程如下:
6、如何判断用户登录
在一个 web 应用中,判断用户是否登录是非常常见的需求,通常可以通过以下几种方法来实现:
- Session 管理:
-
- 在用户登录成功后,将用户信息存储在 Session 中。每次请求到达服务器时,可以从 Session 中获取用户信息判断是否登录。
- 如果用户退出登录,可以清除 Session 中的用户信息。
- 优点:简单、易于实现。
- 缺点:可能存在 Session 过期、共享 Session 集群问题。
- Token 验证:
-
- 在用户登录成功后,生成一个 Token(例如 JWT)并返回给客户端,客户端每次请求都带上这个 Token。
- 服务器端通过验证 Token 的合法性来判断用户是否登录。
- 优点:无状态、可扩展性好。
- 缺点:需要前后端配合,Token 的管理和过期策略需要注意。
- Cookie 验证:
-
- 在用户登录成功后,生成一个包含用户信息的 Cookie 并发送给客户端。
- 客户端每次请求都会携带该 Cookie,服务器端通过验证 Cookie 来判断用户是否登录。
- 优点:相对简单,无需额外的代码。
- 缺点:Cookie 可能被窃取或伪造。
- OAuth 和第三方认证:
-
- 使用 OAuth 2.0 协议和第三方认证服务(如 Google、Facebook、GitHub)来实现用户登录。
- 用户登录后,应用会获得一个访问令牌,用于访问用户数据。
- 优点:减少用户的注册和登录成本。
- 缺点:需要集成第三方服务,用户可能会担心隐私问题。
无论选择哪种方法,都需要根据项目的需求和安全性来做出适当的决策。通常情况下,Session 管理和 Token 验证是比较常用的方式。在具体实现中,您可以结合框架、库或者自行编写代码来实现用户登录的判断和管理逻辑。
在项目中如何实现:
把对session域中的用户对象校验放在SpringMVC的Interceptor当中。
当客户端请求资源的时候,自动把请求拦截到拦截器当中,开始判断session域中是否存在user对象,如果存在则放行,如果不存在则重定向到登录页面。
可以在我之前发布过的这个项目的基础上加上SpringMVC拦截器操作:
黑马程序员SpringMVC练手项目_逐梦苍穹的博客-CSDN博客https://blog.csdn.net/qq_60735796/article/details/132126823
下面是拦截器以及三层架构中对应的代码实现:
LoginInterceptor:
package com.xzl.interceptor;
import com.xzl.domain.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author 逐梦苍穹
* @date 2023/8/12 21:47
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession httpSession = request.getSession();
System.out.println(httpSession);
User user = (User) httpSession.getAttribute("user");
if (user == null){
System.out.println(request.getContextPath());
response.sendRedirect(request.getContextPath() + "/login.jsp");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
UserController:
/**
* @author 逐梦苍穹
* @date 2023/8/5 21:19
*/
@Controller
@RequestMapping(value = "/User")
public class UserController {
@Autowired
@Qualifier(value = "userService")
private UserService userService;
@RequestMapping(value = "/login")
public String login(String username, String password, HttpSession httpSession) {
User user = userService.login(username,password);
if(user!=null){
//登录成功 将user存储到session
httpSession.setAttribute("user",user);
return "redirect:/index.jsp";
}
return "redirect:/login.jsp";
}
}
UserServiceImpl:
/**
* @author 逐梦苍穹
* @date 2023/8/5 21:25
*/
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier(value = "userDao")
private UserDao userDao;
@Override
public User login(String username, String password) {
try {
return userDao.findByUsernameAndPassword(username, password);
}catch (Exception e) {
return null;
}
}
}
UserDaoImpl:
/**
* @author 逐梦苍穹
* @date 2023/8/5 21:30
*/
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
@Autowired
@Qualifier(value = "jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Override
public User findByUsernameAndPassword(String username, String password) {
return jdbcTemplate.queryForObject("select * from sys_user where username=? and password=?", new BeanPropertyRowMapper<User>(User.class), username, password);
}
}