You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot3自定义过滤器场景下SecurityConfig失效问题求助

问题:SpringBoot3中Security自定义过滤器导致/h2-console权限配置失效

问题描述

已为/h2-console/**路径设置permitAll权限,但访问该路径时仍会进入配置的CustomAuthenticationEntryPoint。尝试配置AnonymousAuthenticationFilter也未生效。

相关配置代码

SecurityConfig

@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
    return http
            .httpBasic().disable()
            .formLogin().disable()
            .csrf().disable()
            .headers().frameOptions().disable()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeHttpRequests(
                    authorize -> {
                        try {
                            authorize
                                    .requestMatchers("/h2-console/**").permitAll()
                                    .requestMatchers("/exception/**").permitAll()
                                    .requestMatchers(HttpMethod.POST, "/api/sign-in", "/api/sign-up", "/api/refresh-token").permitAll()
                                    .requestMatchers(HttpMethod.GET, "/api/**").permitAll()
                                    .requestMatchers(HttpMethod.DELETE, "/api/members/{id}/**")
                                    .access((authentication, object) -> {
                                        String idAttr = object.getVariables().get("id");
                                        return new AuthorizationDecision(memberGuard.check(Long.parseLong(idAttr)));
                                    })
                                    .requestMatchers(HttpMethod.GET, "/swagger-ui.html/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**").permitAll()
                                    .anyRequest().hasRole("ADMIN");
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    })
            .exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())
            .and()
            .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
            .and()
            .addFilterBefore(new JwtAuthenticationFilter(accessTokenHelper, userDetailsService),
                    UsernamePasswordAuthenticationFilter.class)
            .build();
}

自定义JwtAuthenticationFilter

@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final TokenHelper accessTokenHelper;
    private final CustomUserDetailsService userDetailsService;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = extractToken(request);
        if(validateToken(token)) {
            setAuthentication(token);
        }
        filterChain.doFilter(request, response);
    }

    private String extractToken(HttpServletRequest request) {
        return request.getHeader("Authorization");
    }

    private boolean validateToken(String token) {
        return token != null && accessTokenHelper.validate(token);
    }

    private void setAuthentication(String token) {
        String userId = accessTokenHelper.extractSubject(token);
        CustomUserDetails userDetails = userDetailsService.loadUserByUsername(userId);
        SecurityContextHolder.getContext().setAuthentication(new CustomAuthenticationToken(userDetails, userDetails.getAuthorities()));
    }

}

CustomUserDetailsService

@Component
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
    private final MemberRepository memberRepository;


    @Override
    public CustomUserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        Member member = memberRepository.findById(Long.valueOf(userId))
                .orElseGet(Member::anonymousMember);
        return new CustomUserDetails(
                String.valueOf(member.getId()),
                member.getRoleSet().stream().map(MemberRole::getAuthority)
                        .map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
    }
}

尝试过的无效配置

.anonymous().authenticationFilter(new AnonymousAuthenticationFilter("key", "anonymousUser",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")))

问题原因

  1. 全局try-catch破坏权限配置逻辑:在authorizeHttpRequests的lambda中包裹全局try-catch,若配置过程中抛出异常(如ID转换失败),会导致权限规则配置不完整,可能使permitAll规则失效,所有请求默认走anyRequest().hasRole("ADMIN"),触发认证入口。
  2. Jwt过滤器未跳过免校验路径:自定义Jwt过滤器会处理所有请求,包括permitAll路径,若请求无有效Token,SecurityContext会保持为空,后续匿名认证逻辑可能因顺序问题无法正常触发。
  3. 手动配置AnonymousAuthenticationFilter干扰默认逻辑:STATELESS模式下Spring Security默认会启用匿名认证,手动配置可能因参数不匹配导致失效。

解决方案

1. 移除全局try-catch,局部处理异常

将权限配置中的异常处理移到access逻辑内部,避免破坏整个权限规则的构建:

.authorizeHttpRequests(authorize -> authorize
        .requestMatchers("/h2-console/**").permitAll()
        .requestMatchers("/exception/**").permitAll()
        .requestMatchers(HttpMethod.POST, "/api/sign-in", "/api/sign-up", "/api/refresh-token").permitAll()
        .requestMatchers(HttpMethod.GET, "/api/**").permitAll()
        .requestMatchers(HttpMethod.DELETE, "/api/members/{id}/**")
        .access((authentication, object) -> {
            try {
                String idAttr = object.getVariables().get("id");
                return new AuthorizationDecision(memberGuard.check(Long.parseLong(idAttr)));
            } catch (NumberFormatException e) {
                return new AuthorizationDecision(false);
            }
        })
        .requestMatchers(HttpMethod.GET, "/swagger-ui.html/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**").permitAll()
        .anyRequest().hasRole("ADMIN")
)

2. 让Jwt过滤器跳过免校验路径

重写shouldNotFilter方法,排除所有permitAll的路径:

@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
    String path = request.getRequestURI();
    String method = request.getMethod();
    return path.startsWith("/h2-console/")
            || path.startsWith("/exception/")
            || (HttpMethod.POST.name().equals(method) && (path.equals("/api/sign-in") || path.equals("/api/sign-up") || path.equals("/api/refresh-token")))
            || (HttpMethod.GET.name().equals(method) && path.startsWith("/api/"))
            || path.startsWith("/swagger-ui.html/")
            || path.startsWith("/v3/api-docs/")
            || path.startsWith("/swagger-ui/")
            || path.startsWith("/swagger-resources/");
}

3. 移除手动配置的AnonymousAuthenticationFilter

STATELESS模式下Spring Security会自动启用匿名认证,无需手动配置,移除相关代码即可恢复默认逻辑。

4. 合并exceptionHandling配置

避免重复调用exceptionHandling(),简化配置:

.exceptionHandling()
    .accessDeniedHandler(new CustomAccessDeniedHandler())
    .authenticationEntryPoint(new CustomAuthenticationEntryPoint())

验证步骤

  1. 按上述修改权限配置和Jwt过滤器代码
  2. 移除手动配置的匿名过滤器
  3. 重启服务后访问/h2-console,应能正常进入且不会触发CustomAuthenticationEntryPoint

内容的提问来源于stack exchange,提问作者bc a

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 16:25:54