
在spring boot应用中集成jwt(json web token)进行认证是常见的做法。然而,默认情况下,如果我们将自定义的jwt认证过滤器直接添加到spring security的过滤器链中,它可能会拦截所有传入的http请求。在某些场景下,我们可能只希望对特定路径(例如restful api接口,通常以/api/开头)进行jwt认证,而让其他路径(如静态资源、登录页面、公共接口等)不受jwt过滤器的影响。本文将介绍如何通过扩展abstractauthenticationprocessingfilter并结合requestmatcher接口来实现这一目标。
Spring Security提供了AbstractAuthenticationProcessingFilter作为处理特定认证请求的抽象基类。这个过滤器在内部使用一个RequestMatcher来决定当前请求是否需要进行认证处理。如果RequestMatcher的matches()方法返回true,则过滤器会尝试处理认证;如果返回false,则直接放行,不进行认证处理。这正是我们实现特定URL过滤的关键。
我们的自定义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。
为了让过滤器只作用于/api/**路径,我们需要创建一个RequestMatcher的实现类,其matches()方法在请求路径匹配/api/**时返回true。
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风格的路径模式。
最后一步是将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();
}
}通过上述配置,您的Spring Boot应用将能够精确地控制JWT认证过滤器的作用范围,仅对/api/**等指定路径进行JWT认证,从而实现更高效、更安全的API访问控制。
以上就是Spring Boot Security中JWT过滤器针对特定URL模式的精准应用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号