SpringBootSecurityJWT过滤器路径控制详解
大家好,今天本人给大家带来文章《Spring Boot Security JWT过滤器精准控制URL路径》,文中内容主要涉及到,如果你对文章方面的知识点感兴趣,那就请各位朋友继续看下去吧~希望能真正帮到你们,谢谢!
1. 问题背景与传统做法的局限性
在Spring Boot应用中,当我们需要为API接口添加JWT认证时,通常会自定义一个JWT认证过滤器,并使用HttpSecurity.addFilterBefore()方法将其添加到Spring Security的过滤器链中。例如:
@Override public void configure(HttpSecurity http) throws Exception { http.addFilterBefore(customJwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); // ... 其他配置 }
这种做法虽然简单,但存在一个问题:customJwtAuthenticationFilter会拦截所有进入Spring Security过滤器的请求。如果我们的应用既有需要认证的API(如/api/**),也有公开访问的页面或接口(如/login, /, /public/**),那么即使是公开接口,也会被JWT过滤器尝试处理,这可能导致不必要的性能开销或逻辑复杂性。
理想情况下,我们希望JWT过滤器只对那些明确需要JWT认证的路径(例如,所有以/api/开头的路径)生效,而对其他路径则直接放行或交由其他过滤器处理。
2. 解决方案:利用 AbstractAuthenticationProcessingFilter 和 RequestMatcher
Spring Security提供了AbstractAuthenticationProcessingFilter,这是一个专门用于处理特定认证流程的抽象基类。它允许我们通过传入一个RequestMatcher实例来精确控制过滤器所处理的请求。当请求的URL与RequestMatcher匹配时,AbstractAuthenticationProcessingFilter才会尝试执行认证逻辑。
2.1 核心组件解析
- AbstractAuthenticationProcessingFilter: 这是Spring Security提供的一个抽象过滤器,设计用于处理特定类型的认证请求。它内部持有一个RequestMatcher,只有当请求与该RequestMatcher匹配时,才会调用其attemptAuthentication()方法来执行认证逻辑。
- RequestMatcher: 这是一个接口,用于判断给定的HttpServletRequest是否匹配某种条件。Spring Security提供了多种实现,如AntPathRequestMatcher(基于Ant风格路径匹配)、RegexRequestMatcher(基于正则表达式)、OrRequestMatcher(多个RequestMatcher中的任意一个匹配即可)等。
2.2 实现步骤
步骤一:修改 CustomJwtAuthenticationFilter
让你的JWT认证过滤器继承AbstractAuthenticationProcessingFilter,并在构造函数中接收一个RequestMatcher实例。同时,你需要重写attemptAuthentication()方法,将你原有的JWT解析和认证逻辑放入其中。
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.RequestMatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; // 假设你有一个JwtTokenProvider来处理JWT的生成和验证 // import com.yourpackage.security.JwtTokenProvider; public class CustomJwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private final UserDetailsService userDetailsService; // private final JwtTokenProvider jwtTokenProvider; // 假设你有一个JWT工具类 // 构造函数:传入RequestMatcher和AuthenticationManager public CustomJwtAuthenticationFilter( RequestMatcher requiresAuthenticationRequestMatcher, AuthenticationManager authenticationManager, UserDetailsService userDetailsService /*, JwtTokenProvider jwtTokenProvider */) { super(requiresAuthenticationRequestMatcher); // 指定哪些请求需要此过滤器处理 setAuthenticationManager(authenticationManager); // 设置认证管理器 this.userDetailsService = userDetailsService; // this.jwtTokenProvider = jwtTokenProvider; } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { // 1. 从请求头中提取JWT令牌 String authorizationHeader = request.getHeader("Authorization"); if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) { // 如果没有Bearer Token,或者格式不正确,则抛出认证异常 // AbstractAuthenticationProcessingFilter 会捕获此异常并调用 AuthenticationFailureHandler throw new BadCredentialsException("Missing or invalid Authorization header"); } String jwtToken = authorizationHeader.substring(7); // 移除 "Bearer " 前缀 // 2. 验证JWT令牌并获取用户信息 // 实际项目中,这里会调用你的JwtTokenProvider来验证令牌并解析出用户名 // 示例: // if (!jwtTokenProvider.validateToken(jwtToken)) { // throw new BadCredentialsException("Invalid JWT token"); // } // String username = jwtTokenProvider.getUsernameFromToken(jwtToken); // 简化示例,直接假设从JWT中解析出用户名 "testuser" String username = "testuser"; // 实际应从JWT中解析 // 3. 根据用户名加载用户详情 UserDetails userDetails = userDetailsService.loadUserByUsername(username); // 4. 创建一个认证令牌并交由AuthenticationManager进行认证 // 注意:这里通常不需要再次调用authenticationManager.authenticate(), // 因为JWT本身就是一种认证凭证。我们直接创建一个已认证的Authentication对象。 // 如果你的JWT验证逻辑非常复杂,需要AuthenticationManager的Provider来处理, // 则可以构造一个UsernamePasswordAuthenticationToken或其他Token,然后调用getAuthenticationManager().authenticate() // 但对于多数JWT场景,直接返回一个已认证的Token即可。 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); // 将请求详情设置到Authentication对象中,以便后续的WebAuthenticationDetailsSource使用 authenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request)); return authenticationToken; // 返回已认证的Authentication对象 } // 可以在这里重写 successfulAuthentication 和 unsuccessfulAuthentication // 以处理认证成功或失败后的逻辑,例如记录日志、设置响应头等。 // @Override // protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { // SecurityContextHolder.getContext().setAuthentication(authResult); // chain.doFilter(request, response); // } // @Override // protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { // // 可以在这里自定义失败响应 // super.unsuccessfulAuthentication(request, response, failed); // } }
步骤二:在 WebSecurityConfigurerAdapter 中配置过滤器
在你的安全配置类中,将CustomJwtAuthenticationFilter声明为一个Spring Bean,并在configure(HttpSecurity http)方法中将其添加到过滤器链。关键在于创建CustomJwtAuthenticationFilter实例时,传入正确的RequestMatcher。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; // 你的UserDetailsService实现 // @Autowired // private JwtTokenProvider jwtTokenProvider; // 你的JWT工具类 // 假设你有一个JWT认证入口点,处理未认证的请求 // @Autowired // private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } // 声明 CustomJwtAuthenticationFilter 为 Spring Bean @Bean public CustomJwtAuthenticationFilter customJwtAuthenticationFilter() throws Exception { // 定义需要JWT过滤器处理的URL模式 // 示例1: 只匹配 /api/** RequestMatcher protectedUrlMatcher = new AntPathRequestMatcher("/api/**"); // 示例2: 匹配多个URL模式 (如 /api/users/** 和 /api/products/**) // List<String> protectedPaths = Arrays.asList("/api/users/**", "/api/products/**"); // RequestMatcher protectedUrlMatcher = new OrRequestMatcher( // protectedPaths.stream() // .map(AntPathRequestMatcher::new) // .collect(Collectors.toList()) // ); // 实例化 CustomJwtAuthenticationFilter,传入RequestMatcher、AuthenticationManager和UserDetailsService // 注意:authenticationManagerBean() 在这里直接调用会抛出异常,因为它需要Spring上下文初始化。 // 正确的做法是将其作为参数注入到 @Bean 方法中,或者在 configure(HttpSecurity) 中获取。 // 这里通过在 @Bean 方法签名中添加 AuthenticationManager authenticationManager 来注入 return new CustomJwtAuthenticationFilter( protectedUrlMatcher, authenticationManagerBean(), // 获取AuthenticationManager实例 userDetailsService /*, jwtTokenProvider */); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() // 禁用CSRF .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // JWT通常是无状态的 .and() .exceptionHandling() // .authenticationEntryPoint(jwtAuthenticationEntryPoint) // 处理未认证的请求 // .accessDeniedPage("/403") // 处理无权限的请求 .and() .authorizeRequests() // 确保被JWT过滤器处理的路径需要认证 .antMatchers("/api/**").authenticated() // 其他路径可以公开访问 .antMatchers("/login", "/", "/public/**").permitAll() .anyRequest().authenticated() // 默认所有其他请求都需要认证 .and() // 将自定义的JWT过滤器添加到UsernamePasswordAuthenticationFilter之前 .addFilterBefore(customJwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // .formLogin() // 如果你还需要表单登录,可以继续配置 // .loginPage("/login") // .defaultSuccessUrl("/users") // .failureUrl("/login?error=true") // .permitAll() // .and() // .logout() // .logoutSuccessUrl("/") // .permitAll(); } }
3. 注意事项与总结
- attemptAuthentication() 方法的实现:这是你JWT认证逻辑的核心。你需要从请求中提取JWT,验证其有效性,并根据令牌中的信息(如用户ID或用户名)加载用户详情(UserDetails)。最后,创建一个已认证的Authentication对象并返回。
- AuthenticationManager 的注入:AbstractAuthenticationProcessingFilter 需要一个AuthenticationManager来处理认证。在@Bean方法中,Spring会自动注入AuthenticationManager实例。
- authorizeRequests() 的配合:即使你的CustomJwtAuthenticationFilter已经通过RequestMatcher过滤了请求,HttpSecurity.authorizeRequests()中的antMatchers("/api/**").authenticated()仍然是必不可少的。前者负责“尝试”认证(如果请求匹配),后者负责“强制”认证(如果请求需要认证但未认证)。两者协同工作,确保只有通过JWT认证的请求才能访问/api/**路径。
- 错误处理:AbstractAuthenticationProcessingFilter在认证失败时会抛出AuthenticationException。你可以通过设置AuthenticationFailureHandler来自定义认证失败后的行为(例如返回特定的JSON错误响应)。
- 会话管理:对于JWT认证,通常会将会话管理设置为STATELESS,因为JWT本身包含了认证信息,服务器无需维护会话状态。
- 依赖注入:确保你的CustomJwtAuthenticationFilter所依赖的其他服务(如UserDetailsService, JwtTokenProvider)能够正确地通过Spring的依赖注入机制获取到。
通过以上配置,你的JWT过滤器将只会对/api/**路径下的请求进行处理,而其他路径(如/login、/等)将不再经过JWT过滤器的认证逻辑,从而实现更精准、高效的安全控制。
好了,本文到此结束,带大家了解了《SpringBootSecurityJWT过滤器路径控制详解》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多文章知识!

- 上一篇
- Golang责任链模式实现与请求处理演示

- 下一篇
- Python优化内存:ijson流式解析大文件方法
-
- 文章 · java教程 | 8小时前 |
- JavaProperties快速获取指定键值方法
- 350浏览 收藏
-
- 文章 · java教程 | 8小时前 |
- volatile关键字详解与使用场景
- 453浏览 收藏
-
- 文章 · java教程 | 10小时前 |
- Java代理模式三种实现方式详解
- 245浏览 收藏
-
- 文章 · java教程 | 10小时前 |
- SpringBoot集成Prometheus监控教程
- 402浏览 收藏
-
- 文章 · java教程 | 10小时前 |
- Spring Security OAuth2单点登录实现教程
- 182浏览 收藏
-
- 文章 · java教程 | 11小时前 |
- Java处理JSON:Jackson与Gson对比详解
- 382浏览 收藏
-
- 文章 · java教程 | 11小时前 |
- Java操作JSON,org.json库入门指南
- 384浏览 收藏
-
- 文章 · java教程 | 11小时前 |
- JavaDNS库高效解析主机技巧
- 378浏览 收藏
-
- 文章 · java教程 | 11小时前 |
- GoogleCloudFunction旧状态保存技巧
- 422浏览 收藏
-
- 文章 · java教程 | 12小时前 |
- Android子Activity如何调用父Activity数据和方法
- 433浏览 收藏
-
- 文章 · java教程 | 12小时前 |
- JavaStreamPredicate类型错误解决方法
- 466浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 366次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 364次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 354次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 365次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 384次使用
-
- 提升Java功能开发效率的有力工具:微服务架构
- 2023-10-06 501浏览
-
- 掌握Java海康SDK二次开发的必备技巧
- 2023-10-01 501浏览
-
- 如何使用java实现桶排序算法
- 2023-10-03 501浏览
-
- Java开发实战经验:如何优化开发逻辑
- 2023-10-31 501浏览
-
- 如何使用Java中的Math.max()方法比较两个数的大小?
- 2023-11-18 501浏览