杰瑞科技汇

Java拦截器如何实现页面跳转?

Spring MVC 拦截器Servlet 过滤器,并提供完整的代码示例。

Java拦截器如何实现页面跳转?-图1
(图片来源网络,侵删)

核心概念

  1. 目标: 在用户访问某个需要权限的页面(如 /user/profile)之前,检查他是否已经登录。
  2. 判断: 如果未登录,则拦截该请求,并跳转到登录页面(/login)。
  3. 实现手段:
    • 转发: request.getRequestDispatcher("/login").forward(request, response);
      • 特点: 浏览器地址栏不会改变,仍然是原始请求的 URL,属于服务器内部行为。
    • 重定向: response.sendRedirect("/login");
      • 特点: 浏览器地址栏改变为新的 URL (/login),服务器告诉浏览器:“你去访问这个新地址吧”,属于客户端行为。

Spring MVC 拦截器

这是在 Spring 项目中最推荐、最优雅的方式,Spring MVC 提供了 HandlerInterceptor 接口,让我们可以方便地实现拦截逻辑。

实现拦截器类

创建一个类,实现 HandlerInterceptor 接口。

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component // 将拦截器交给 Spring 管理
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 在请求处理之前进行调用(Controller 方法调用之前)
     * @return true: 继续流程 (如调用下一个拦截器或Controller)
     *         false: 终止流程,不会调用后续的拦截器和Controller,此时我们需要自己通过 response 来产生响应
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LoginInterceptor.preHandle() 被调用...");
        // 1. 从 session 中获取用户信息
        Object user = request.getSession().getAttribute("user");
        // 2. 判断用户是否已登录
        if (user == null) {
            System.out.println("用户未登录,进行拦截并跳转到登录页...");
            // 使用转发
            // request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
            // 使用重定向 (推荐,因为重定向后 URL 会变,符合登录页的语义)
            // 注意:如果使用了 Thymeleaf 等模板引擎,路径可能是 /login
            response.sendRedirect(request.getContextPath() + "/login");
            // 返回 false,表示拦截请求,不再调用 Controller
            return false;
        }
        // 3. 用户已登录,放行
        System.out.println("用户已登录,放行请求...");
        return true;
    }
    /**
     * 请求处理之后进行调用(Controller 方法调用之后,视图渲染之前)
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 通常用于对 ModelAndView 进行处理,比如修改数据、添加公共信息等
        System.out.println("LoginInterceptor.postHandle() 被调用...");
    }
    /**
     * 整个请求结束之后被调用(主要用于资源清理工作)
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("LoginInterceptor.afterCompletion() 被调用...");
    }
}

代码解释:

  • @Component: 将这个类声明为 Spring 的一个 Bean,这样 Spring 才能扫描并管理它。
  • preHandle(): 这是拦截器的核心方法,我们在这里进行登录判断。
  • request.getSession().getAttribute("user"): 尝试从 Session 中获取登录用户信息,这个 user 对象通常是在用户成功登录后放入 Session 的。
  • response.sendRedirect(...): 如果用户未登录,执行重定向。request.getContextPath() 可以获取当前项目的根路径(如 /my-project),避免了硬编码。
  • return false;: 关键! 返回 false 会告诉 Spring 停止后续流程,即不再调用 Controller 方法。
  • return true;: 返回 true 表示放行,请求会继续向下传递。

注册拦截器

光有拦截器类还不行,你需要告诉 Spring 哪些 URL 需要被这个拦截器拦截,这通常在配置类中完成。

Java拦截器如何实现页面跳转?-图2
(图片来源网络,侵删)

如果你使用的是 Spring Boot,可以创建一个配置类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/user/**")  // 拦截所有以 /user/ 开头的请求
                .excludePathPatterns("/user/login", "/user/register"); // 排除登录和注册的请求,否则登录请求也会被拦截
    }
}

代码解释:

  • addInterceptor(): 注册我们创建的 LoginInterceptor
  • .addPathPatterns("/user/**"): 设置拦截规则,这里表示拦截所有以 /user/ 开头的路径。
  • .excludePathPatterns(...): 设置排除规则,这些路径不会被拦截。非常重要,否则你的登录请求和注册请求也会被拦截,导致死循环。

Servlet 过滤器

过滤器是 Servlet 规范的一部分,它工作在更底层的请求处理流程中,在 Spring 项目中,它通常在 Spring 拦截器之前执行。

实现过滤器类

创建一个类,实现 javax.servlet.Filter 接口。

Java拦截器如何实现页面跳转?-图3
(图片来源网络,侵删)
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
// @WebFilter(urlPatterns = "/user/*") // 也可以直接用注解配置,但推荐在 web.xml 或注册类中配置
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 过滤器初始化时调用
        System.out.println("LoginFilter 初始化...");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("LoginFilter.doFilter() 被调用...");
        // 将 ServletRequest 转换为 HttpServletRequest,以便使用 session 等功能
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 1. 获取请求的 URI
        String requestURI = request.getRequestURI();
        System.out.println("当前请求 URI: " + requestURI);
        // 2. 获取 session
        HttpSession session = request.getSession();
        // 3. 判断是否是登录或注册相关的请求,如果是则直接放行
        if (requestURI.contains("/login") || requestURI.contains("/register")) {
            System.out.println("是登录/注册请求,直接放行...");
            filterChain.doFilter(request, response); // 继续执行后续的过滤器或 Servlet
            return;
        }
        // 4. 判断用户是否已登录
        Object user = session.getAttribute("user");
        if (user == null) {
            System.out.println("用户未登录,进行拦截并跳转到登录页...");
            // 使用转发
            // request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
            // 使用重定向
            response.sendRedirect(request.getContextPath() + "/login");
            return; // 终止执行
        }
        // 5. 用户已登录,放行
        System.out.println("用户已登录,放行请求...");
        filterChain.doFilter(request, response); // 继续执行后续的过滤器或 Servlet
    }
    @Override
    public void destroy() {
        // 过滤器销毁时调用
        System.out.println("LoginFilter 销毁...");
    }
}

代码解释:

  • doFilter(): 这是过滤器的核心方法。
  • filterChain.doFilter(request, response): 关键! 这行代码的作用是“放行”,将请求传递给下一个过滤器或目标 Servlet(Controller),如果不调用,请求链就会被中断。
  • return;: 在执行了重定向/转发后,一定要 return,否则代码会继续向下执行,错误地调用 filterChain.doFilter()

注册过滤器

在 Spring Boot 中,你可以通过两种方式注册过滤器:

方式 A:使用 @WebFilter 注解 (简单)

在过滤器类上直接添加 @WebFilter 注解。

import javax.servlet.annotation.WebFilter;
@WebFilter(urlPatterns = "/user/*") // 拦截所有 /user/* 下的请求
public class LoginFilter implements Filter {
    // ... 上面 doFilter 中的代码 ...
}

这种方式简单,但不够灵活,比如很难设置多个过滤器的执行顺序。

方式 B:编程式注册 (推荐)

创建一个配置类,实现 WebMvcConfigurerServletListenerRegistrationBean

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<LoginFilter> loginFilterRegistration() {
        FilterRegistrationBean<LoginFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new LoginFilter());
        registrationBean.addUrlPatterns("/user/*"); // 设置拦截路径
        registrationBean.setOrder(1); // 设置过滤器执行顺序,数字越小越先执行
        return registrationBean;
    }
}

这种方式更灵活,可以精确控制过滤器的顺序和参数。


总结与对比

特性 Spring MVC 拦截器 Servlet 过滤器
规范 Spring 框架特有 Java EE (Servlet) 规范,通用
作用范围 仅在 Spring MVC 环境下工作 在任何支持 Servlet 的容器下工作
执行时机 在过滤器之后,在 Controller 之前/之后执行 在拦截器之前,在请求到达应用时最早执行
获取信息 可以获取到 Handler (Controller 方法实例) 无法获取到 Handler
主要用途 权限验证、日志记录(针对 Controller 方法)、数据预处理等 字符编码转换、敏感词过滤、通用日志记录(针对所有请求)
配置方式 实现 WebMvcConfigureraddInterceptors 方法 @WebFilter 注解或 FilterRegistrationBean

如何选择?

  • 优先选择 Spring MVC 拦截器:如果你的项目是 Spring/Spring Boot 项目,并且你的拦截逻辑与具体的 Controller 方法相关(比如根据方法上的注解判断权限),那么拦截器是最佳选择,它与 Spring 生态结合更紧密,使用更方便。
  • 考虑使用 Servlet 过滤器:如果你的拦截逻辑是全局的、与框架无关的,比如设置字符集(CharacterEncodingFilter)、处理跨域、或者你想在 Spring 容器初始化之前就处理请求,那么应该使用过滤器。

对于登录验证这种典型的业务逻辑,Spring MVC 拦截器 是更常见和推荐的做法。

分享:
扫描分享到社交APP
上一篇
下一篇