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

响应式编程下Spring Security过滤器仅登录后调用优化问询

WebFlux权限过滤器优化方案

核心优化方向

  • 消除配置重复:过滤器与Security配置中重复定义的允许URL集合,统一维护避免不一致
  • 简化流操作:小集合无需使用parallelStream,改用普通Stream API提升效率
  • 移除冗余原子类:Reactor订阅逻辑为单线程执行,无需AtomicReference/AtomicInteger保证线程安全
  • 优化URL匹配:利用Spring原生PathMatcher统一处理通配符匹配,替代手动字符串切割
  • 简化权限校验:用anyMatch替代计数判断逻辑,提升代码可读性与性能
  • 完善边界处理:增加未认证场景的兜底逻辑

优化后的CustomerWebFilter代码

@Log4j2
@Component
@RequiredArgsConstructor
public class CustomerWebFilter implements WebFilter {

    private final AccountRoleMenuViewEntityService accountRoleMenuViewEntityService;
    private final PathMatcher pathMatcher = new AntPathMatcher();

    // 复用Security配置中的免认证规则,避免重复定义
    private static final List<String> PERMIT_ALL_PATTERNS = Arrays.asList(
            "/login", "/logout", "/forgotpassword", "/sendcode", "/verifycode",
            "/newpassword", "/razorpay-webhook", "/assets/**", "/bootstrap/**", "/plugins/**"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String requestPath = request.getPath().pathWithinApplication().value();

        // 跳过免认证URL
        if (PERMIT_ALL_PATTERNS.stream().anyMatch(pattern -> pathMatcher.match(pattern, requestPath))) {
            return chain.filter(exchange);
        }

        log.debug("执行权限过滤,请求路径: {}", requestPath);
        String urlPrefix = extractUrlPrefix(requestPath);

        return exchange.getPrincipal()
                // 兜底处理未认证场景
                .switchIfEmpty(Mono.defer(() -> {
                    response.setStatusCode(HttpStatus.UNAUTHORIZED);
                    return response.setComplete();
                }))
                .flatMap(principal -> {
                    log.debug("当前用户: {}", principal.getName());
                    // 根路径默认匹配dashboard前缀
                    String targetPrefix = "/".equals(urlPrefix) ? "/customer/dashboard" : urlPrefix;

                    return accountRoleMenuViewEntityService.findByUsername(principal.getName())
                            // 检查是否存在匹配的权限前缀
                            .anyMatch(menu -> {
                                String controller = menu.getController();
                                if (controller.contains("/") && controller.split("/").length >=3) {
                                    String controllerPrefix = "/" + controller.split("/")[1] + "/" + controller.split("/")[2];
                                    log.debug("控制器前缀: {}, 请求前缀: {}", controllerPrefix, targetPrefix);
                                    return controllerPrefix.equalsIgnoreCase(targetPrefix);
                                }
                                return false;
                            })
                            .flatMap(hasPermission -> {
                                if (!hasPermission) {
                                    response.setStatusCode(HttpStatus.FORBIDDEN);
                                    return response.setComplete();
                                }
                                return chain.filter(exchange);
                            });
                });
    }

    /**
     * 提取URL二级前缀,例如 /customer/order/list -> /customer/order
     */
    private String extractUrlPrefix(String requestPath) {
        if ("/".equals(requestPath) || requestPath.length() <= 1) {
            return "/";
        }
        String[] pathSegments = requestPath.split("/");
        return pathSegments.length >=3 ? "/" + pathSegments[1] + "/" + pathSegments[2] : requestPath;
    }
}

配套优化的Security配置

新增统一常量类维护权限规则,彻底消除配置重复:

// 统一权限规则常量类
public class SecurityConstants {
    public static final List<String> PERMIT_ALL_PATTERNS = Arrays.asList(
            "/login", "/logout", "/forgotpassword", "/sendcode", "/verifycode",
            "/newpassword", "/razorpay-webhook", "/assets/**", "/bootstrap/**", "/plugins/**"
    );
}

修改Security配置类引用常量:

@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
public class ConfigurationSecurity {

    private final CustomerWebFilter customerWebFilter;
    private final RememberMeSuccessHandler rememberMeSuccessHandler;

    @Bean
    public SecurityWebFilterChain configure(ServerHttpSecurity http) {
        http.csrf().disable()
            .headers().frameOptions().disable()
            .and()
            .authorizeExchange()
                .pathMatchers(SecurityConstants.PERMIT_ALL_PATTERNS.toArray(new String[0])).permitAll()
                .pathMatchers("/customer/**").hasRole("CUSTOMER")
                .anyExchange().authenticated()
            .and()
            .addFilterAfter(customerWebFilter, SecurityWebFiltersOrder.AUTHORIZATION)
            .formLogin()
                .loginPage("/login")
                .authenticationSuccessHandler(rememberMeSuccessHandler)
                .authenticationFailureHandler(new AuthenticationFailureHandler())
            .and()
            .exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler())
            .and()
            .logout()
                .logoutUrl("/logout")
                .requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout"))
            .and()
            .httpBasic();
        return http.build();
    }

    @Bean
    ReactiveAuthenticationManager reactiveAuthenticationManager(AccountDataService accountDataService) {
        return new UserDetailsRepositoryReactiveAuthenticationManager(accountDataService);
    }

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

优化后优势

  1. 配置一致性:所有免认证URL规则集中维护,过滤器与Security配置共用,避免维护多份导致的不一致
  2. 高效匹配:用Spring原生AntPathMatcher处理通配符匹配,替代手动字符串切割逻辑
  3. 逻辑简洁:用anyMatch直接判断权限,替代原有的计数统计,代码更直观、性能更好
  4. 健壮性提升:增加未认证场景的兜底处理,返回401状态码,覆盖边界情况
  5. 性能优化:移除不必要的原子类,减少线程安全开销;小集合改用普通Stream操作,避免并行流的额外消耗
  6. 可读性增强:提取URL前缀处理方法,代码结构更清晰

内容的提问来源于stack exchange,提问作者Mitva Shah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 05:54:23