深入理解 JWT、过滤器与拦截器:构建安全高效的 Web 应用

177 阅读6分钟

在现代 Web 开发中,认证授权、请求处理是核心环节。JWT、过滤器(Filter)、拦截器(Interceptor)作为后端开发的常用技术,常常被组合使用来解决身份验证、权限控制、请求预处理等问题。但很多开发者对它们的作用、区别和配合方式一知半解。今天我们就来系统梳理这三个技术,搞清楚它们的底层逻辑和实践场景。

一、JWT:无状态的身份令牌

首先从 JWT 说起。JWT 全称是 JSON Web Token,它是一种紧凑、自包含的令牌格式,用于在网络各方之间安全传递信息。简单来说,JWT 就是一个 “数字身份证”,能让服务器快速验证请求者的身份。

1. JWT 的结构:3 部分组成的字符串

JWT 本质是一个由.分隔的字符串,分为三部分:

  • 头部(Header) :指定令牌类型(JWT)和签名算法(如 HS256、RS256),经 Base64 编码后作为第一部分。
  • 载荷(Payload) :存储需要传递的核心信息(如用户 ID、角色、过期时间等),同样经 Base64 编码(注意:Base64 是编码不是加密,不要存敏感信息)。
  • 签名(Signature) :用头部指定的算法,结合密钥对 “头部 + 载荷” 进行加密生成的字符串,用于验证令牌是否被篡改。

2. JWT 的工作流程

JWT 的核心价值是 “无状态认证”,流程如下:

  1. 登录生成令牌:用户提交账号密码,服务器验证通过后,生成包含用户信息(如 ID、角色)和过期时间的 JWT,返回给客户端。
  2. 后续请求携带令牌:客户端(如浏览器、APP)在后续请求中,通过Authorization请求头(格式:Bearer <token>)携带 JWT。
  3. 服务器验证令牌:服务器接收请求后,解析 JWT 的签名,验证令牌是否有效(未过期、未被篡改),验证通过则认可用户身份。

二、过滤器(Filter):请求的 “第一道关卡”

过滤器是 Servlet 规范定义的组件,作用于请求进入 Servlet 之前、响应返回客户端之后,用于对请求 / 响应进行通用处理。

优势:服务器无需存储令牌(无状态),适合分布式系统;自包含信息减少数据库查询。
局限:令牌一旦签发无法主动撤销(除非结合黑名单);载荷仅 Base64 编码,不能存敏感信息。

1. 核心特性

  • 基于函数回调:依赖 Servlet 容器,与 Spring 框架无关。

  • 作用范围广:可处理所有请求(包括静态资源、非 Spring MVC 请求)。

  • 生命周期:随 Web 应用启动初始化,随应用停止销毁;每次请求触发doFilter方法。

常见用途:编码转换(如设置UTF-8)、日志记录、Token 验证(如 JWT 校验)、跨域处理(CORS)等。

2. 实现方式

自定义过滤器需实现javax.servlet.Filter接口,重写三个方法:

  • init():初始化方法,Web 应用启动时执行。

  • doFilter():核心方法,处理请求 / 响应,通过chain.doFilter(request, response)放行请求。

  • destroy():销毁方法,Web 应用停止时执行。 示例:用过滤器验证 JWT

java

public class JwtFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // 1. 获取请求头中的Token
        String token = httpRequest.getHeader("Authorization");
        if (token == null || !token.startsWith("Bearer ")) {
            httpResponse.setStatus(401);
            httpResponse.getWriter().write("未携带有效Token");
            return;
        }
        token = token.substring(7); // 去除"Bearer "前缀
        
        // 2. 验证JWT
        try {
            JwtUtils.verify(token); // 自定义JWT验证工具类
            chain.doFilter(request, response); // 验证通过,放行
        } catch (Exception e) {
            httpResponse.setStatus(401);
            httpResponse.getWriter().write("Token无效或已过期");
        }
    }
}

java

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<JwtFilter> jwtFilter() {
        FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new JwtFilter());
        registrationBean.addUrlPatterns("/api/*"); // 拦截/api/*路径的请求
        registrationBean.setOrder(1); // 过滤器执行顺序(数字越小越先执行)
        return registrationBean;
    }
}

三、拦截器(Interceptor):Controller 的 “贴身保镖”

拦截器是 Spring MVC 提供的组件,作用于 Controller 方法调用前后,依赖 Spring 容器,更贴近业务逻辑处理。

1. 核心特性

  • 基于 AOP 思想:属于 Spring 框架,可访问 Spring 上下文(如ApplicationContextBean)。

  • 作用范围窄:仅处理 Spring MVC 的请求(即通过DispatcherServlet的请求)。

  • 细粒度控制:可获取HandlerMethod(Controller 方法对象),能针对具体方法做处理。

常见用途:权限校验(如验证用户是否有某接口的访问权限)、性能监控(记录接口执行时间)、日志埋点等。

2. 实现方式

自定义拦截器需实现org.springframework.web.servlet.HandlerInterceptor接口,重写三个方法:

  • preHandle():Controller 方法执行前调用,返回true放行,false拦截。

  • postHandle():Controller 方法执行后、视图渲染前调用。

  • afterCompletion():视图渲染后、响应返回前调用(常用于资源清理)。

示例:用拦截器验证用户权限

java

public class PermissionInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
        // 1. 判断是否为Controller方法(排除静态资源等)
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        
        // 2. 获取接口所需权限(假设通过@RequiredPermission注解定义)
        RequiredPermission annotation = handlerMethod.getMethodAnnotation(RequiredPermission.class);
        if (annotation == null) {
            return true; // 无权限注解,直接放行
        }
        String requiredPerm = annotation.value();
        
        // 3. 从请求中获取当前用户权限(假设JWT验证后已将用户信息存入request)
        User user = (User) request.getAttribute("currentUser");
        if (user == null || !user.getPermissions().contains(requiredPerm)) {
            response.setStatus(403);
            response.getWriter().write("无访问权限");
            return false; // 拦截请求
        }
        return true; // 权限通过,放行
    }
}

注册拦截器(需配置在 Spring MVC 中):

java

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PermissionInterceptor())
                .addPathPatterns("/api/**") // 拦截/api/*路径
                .excludePathPatterns("/api/login"); // 排除登录接口
    }
}

过滤器与拦截器的核心区别

很多人会混淆过滤器和拦截器,核心区别如下:

维度过滤器(Filter)拦截器(Interceptor)
依赖Servlet 容器(与 Spring 无关)Spring MVC 框架
作用时机请求进入 Servlet 之前Controller 方法执行前后
处理范围所有请求(包括非 Spring 请求)仅 Spring MVC 请求
可访问对象ServletRequest/ServletResponse可访问HandlerMethod、Spring 上下文
执行顺序随注册顺序执行(Order值越小越先)随注册顺序执行(Order值越小越先)

四、三者结合:构建完整的认证授权流程

在实际开发中,JWT、过滤器、拦截器常常配合使用,形成 “认证 - 授权” 的完整链路:

  1. 过滤器负责 JWT 验证:请求进入时,过滤器先校验 JWT 是否有效。无效则直接返回 401;有效则解析用户信息(如用户 ID、角色),存入request属性中。
  2. 拦截器负责权限校验:过滤器放行后,拦截器在preHandle中获取request中的用户信息,结合HandlerMethod判断用户是否有当前接口的访问权限。无权限则返回 403;有权限则放行至 Controller。
  3. Controller 处理业务:经过认证和授权后,Controller 专注于业务逻辑处理。