首页 > Java > java教程 > 正文

Spring Boot Security中JWT过滤器针对特定URL模式的精准应用

碧海醫心
发布: 2025-07-08 19:42:13
原创
565人浏览过

Spring Boot Security中JWT过滤器针对特定URL模式的精准应用

本文旨在详细阐述如何在Spring Boot Security框架中,利用AbstractAuthenticationProcessingFilter和RequestMatcher机制,实现JWT认证过滤器只应用于特定URL模式(如/api/**)而非所有请求。通过构建自定义的请求匹配器并将其集成到过滤器中,开发者可以实现更精细化的安全控制,有效避免不必要的认证处理,从而优化系统性能并提升安全性。

在spring boot应用中集成jwt(json web token)进行认证是常见的做法。然而,默认情况下,如果我们将自定义的jwt认证过滤器直接添加到spring security的过滤器链中,它可能会拦截所有传入的http请求。在某些场景下,我们可能只希望对特定路径(例如restful api接口,通常以/api/开头)进行jwt认证,而让其他路径(如静态资源、登录页面、公共接口等)不受jwt过滤器的影响。本文将介绍如何通过扩展abstractauthenticationprocessingfilter并结合requestmatcher接口来实现这一目标。

1. 理解核心组件:AbstractAuthenticationProcessingFilter与RequestMatcher

Spring Security提供了AbstractAuthenticationProcessingFilter作为处理特定认证请求的抽象基类。这个过滤器在内部使用一个RequestMatcher来决定当前请求是否需要进行认证处理。如果RequestMatcher的matches()方法返回true,则过滤器会尝试处理认证;如果返回false,则直接放行,不进行认证处理。这正是我们实现特定URL过滤的关键。

2. 构建自定义JWT认证过滤器

我们的自定义JWT认证过滤器CustomJwtAuthenticationFilter需要继承AbstractAuthenticationProcessingFilter。关键在于其构造函数,它必须接收一个RequestMatcher实例。

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomJwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    // 假设您有一个JWT解析和验证的服务
    private final JwtTokenProvider jwtTokenProvider;

    public CustomJwtAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher, JwtTokenProvider jwtTokenProvider, AuthenticationManager authenticationManager) {
        super(requiresAuthenticationRequestMatcher); // 将RequestMatcher传递给父类
        this.jwtTokenProvider = jwtTokenProvider;
        setAuthenticationManager(authenticationManager); // 设置认证管理器
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
        // 从请求头中获取JWT Token
        String token = jwtTokenProvider.resolveToken(request);

        if (token != null && jwtTokenProvider.validateToken(token)) {
            // 如果Token有效,则构建一个认证对象
            // 这里的AuthenticationToken需要根据您的JWT实现来定义
            // 例如,可以是一个包含用户信息的UsernamePasswordAuthenticationToken
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            return getAuthenticationManager().authenticate(authentication); // 交给AuthenticationManager进行认证
        }
        // 如果没有Token或Token无效,则抛出认证异常
        throw new AuthenticationException("JWT Token is missing or invalid") {};
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        // 认证成功后,将认证信息设置到SecurityContext中,并继续过滤器链
        // SecurityContextHolder.getContext().setAuthentication(authResult); // AbstractAuthenticationProcessingFilter 内部会处理
        chain.doFilter(request, response);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        // 认证失败处理,例如返回401 Unauthorized
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().write("Authentication Failed: " + failed.getMessage());
    }
}
登录后复制

注意:上述代码中的JwtTokenProvider是一个假设的服务,用于解析和验证JWT。getAuthentication()方法应根据您的JWT结构返回一个Authentication对象,通常是UsernamePasswordAuthenticationToken。

3. 实现自定义的RequestMatcher

为了让过滤器只作用于/api/**路径,我们需要创建一个RequestMatcher的实现类,其matches()方法在请求路径匹配/api/**时返回true。

无阶未来模型擂台/AI 应用平台
无阶未来模型擂台/AI 应用平台

无阶未来模型擂台/AI 应用平台,一站式模型+应用平台

无阶未来模型擂台/AI 应用平台 35
查看详情 无阶未来模型擂台/AI 应用平台
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import jakarta.servlet.http.HttpServletRequest;

public class ApiPathRequestMatcher implements RequestMatcher {

    private final AntPathRequestMatcher apiPathMatcher;

    public ApiPathRequestMatcher(String pattern) {
        this.apiPathMatcher = new AntPathRequestMatcher(pattern);
    }

    @Override
    public boolean matches(HttpServletRequest request) {
        // 如果请求路径匹配指定的模式,则返回true,表示需要进行认证处理
        return apiPathMatcher.matches(request);
    }
}
登录后复制

这里我们使用了Spring Security内置的AntPathRequestMatcher来方便地匹配Ant风格的路径模式。

4. 在Spring Security配置中集成过滤器

最后一步是将CustomJwtAuthenticationFilter和ApiPathRequestMatcher集成到Spring Security的配置中。

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.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; // 或者其他认证入口点

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    private final JwtTokenProvider jwtTokenProvider; // 您的JWT提供者服务
    private final BasicAuthenticationEntryPoint jwtAuthenticationEntryPoint; // 认证入口点,用于未认证访问时的处理

    public WebSecurityConfig(JwtTokenProvider jwtTokenProvider, BasicAuthenticationEntryPoint jwtAuthenticationEntryPoint) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public CustomJwtAuthenticationFilter customJwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        // 创建只匹配/api/**路径的请求匹配器
        ApiPathRequestMatcher apiMatcher = new ApiPathRequestMatcher("/api/**");
        return new CustomJwtAuthenticationFilter(apiMatcher, jwtTokenProvider, authenticationManager);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, CustomJwtAuthenticationFilter customJwtAuthenticationFilter) throws Exception {
        http.csrf().disable() // 禁用CSRF
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // JWT通常是无状态的
            .and()
            .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint) // 未认证访问时的处理
                .accessDeniedPage("/403") // 访问被拒绝时的页面
            .and()
            .authorizeRequests()
                // /api/** 路径将由我们的CustomJwtAuthenticationFilter进行认证,
                // 但这里仍然需要配置其授权规则(例如,认证后才能访问)
                .antMatchers("/api/**").authenticated()
                // 其他路径允许所有访问
                .anyRequest().permitAll()
            .and()
            // 如果您的应用同时有表单登录,可以保留以下配置
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/users")
                .failureUrl("/login?error=true")
                .permitAll()
            .and()
            .logout()
                .logoutSuccessUrl("/")
                .permitAll()
            .and()
            // 将我们的自定义JWT过滤器添加到UsernamePasswordAuthenticationFilter之前
            // 这样JWT认证会在Spring Security的默认表单认证之前执行
            .addFilterBefore(customJwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}
登录后复制

5. 注意事项与总结

  • 过滤器顺序:通过addFilterBefore(customJwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class),我们确保了自定义的JWT过滤器在Spring Security默认的用户名密码认证过滤器之前执行。这对于API认证场景是合适的,因为我们希望首先尝试JWT认证。
  • authorizeRequests()与自定义过滤器:即使CustomJwtAuthenticationFilter负责认证/api/**路径,authorizeRequests().antMatchers("/api/**").authenticated()仍然是必要的。前者处理“认证”过程(验证凭据并识别用户),后者处理“授权”过程(认证后用户是否有权限访问该资源)。如果JWT认证成功,用户将被认证,然后authorizeRequests()会根据其认证状态(authenticated())允许访问。
  • 无状态会话:JWT认证通常是无状态的,因此将sessionCreationPolicy(SessionCreationPolicy.STATELESS)设置为无状态是最佳实践,这会告诉Spring Security不要创建或使用HTTP会话来存储安全上下文。
  • 异常处理:authenticationEntryPoint用于处理未认证用户尝试访问受保护资源的情况。accessDeniedPage用于处理已认证用户但无权访问特定资源的情况。
  • JwtTokenProvider和AuthenticationManager:在实际应用中,您需要实现JwtTokenProvider来处理JWT的生成、解析和验证,并配置AuthenticationManager来处理认证逻辑(例如,通过UserDetailsService加载用户详情)。

通过上述配置,您的Spring Boot应用将能够精确地控制JWT认证过滤器的作用范围,仅对/api/**等指定路径进行JWT认证,从而实现更高效、更安全的API访问控制。

以上就是Spring Boot Security中JWT过滤器针对特定URL模式的精准应用的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号