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

SpringBoot Webflux如何忽略带空Authorization头的请求

解决WebFlux Security中非保护路径因空Authorization头返回401的问题

当前配置通过WebFlux Security保护/api/rest/**路径,其余路径无需授权,但请求方发送空Authorization头时,非保护端点会返回401状态码。这是因为HttpBasic认证过滤器会处理所有请求的Authorization头,空头会触发认证失败逻辑。以下是针对WebFlux环境的解决方案:

方案一:限定HttpBasic认证仅作用于受保护路径

通过securityMatcher指定认证逻辑只对/api/rest/**路径生效,其他路径不会触发HttpBasic认证处理:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Value("${...}")
    private String user;
    @Value("${...}")
    private String pw;

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        UserDetails user = User
                .withUsername(user)
                .password(encoder.encode(pw))
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(user);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // 仅对/api/rest/**路径应用安全规则
        http.securityMatcher(ServerWebExchangeMatchers.pathMatchers("/api/rest/**"))
                .authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
                .httpBasic(withDefaults());
        
        // 其他路径直接放行,不经过安全过滤器链
        http.addFilterBefore((exchange, chain) -> chain.filter(exchange), SecurityWebFiltersOrder.FIRST)
                .securityMatcher(ServerWebExchangeMatchers.pathMatchers("/**").negate())
                .authorizeExchange(exchanges -> exchanges.anyExchange().permitAll());
        
        return http.build();
    }
}

方案二:自定义认证入口点忽略非保护路径的认证错误

自定义AuthenticationEntryPoint,当认证失败时判断请求路径是否属于受保护范围,若不属于则返回正常响应而非401:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Value("${...}")
    private String user;
    @Value("${...}")
    private String pw;

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        UserDetails user = User
                .withUsername(user)
                .password(encoder.encode(pw))
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(user);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange(exchanges -> exchanges
                        .pathMatchers("/api/rest/**").authenticated()
                        .anyExchange().permitAll()
                )
                .httpBasic(httpBasic -> httpBasic
                        .authenticationEntryPoint(customAuthenticationEntryPoint())
                );
        return http.build();
    }

    private AuthenticationEntryPoint customAuthenticationEntryPoint() {
        return (exchange, ex) -> {
            // 判断当前请求路径是否受保护
            boolean isProtectedPath = exchange.getRequest().getPath().value().startsWith("/api/rest/");
            if (isProtectedPath) {
                // 受保护路径返回401
                return ServerHttpBasicAuthenticationEntryPoint.BASIC_AUTHENTICATION_ENTRY_POINT.commence(exchange, ex);
            } else {
                // 非保护路径返回正常响应
                exchange.getResponse().setStatusCode(HttpStatus.OK);
                return exchange.getResponse().setComplete();
            }
        };
    }
}

方案三:前置过滤器移除空Authorization头

添加一个前置过滤器,在认证逻辑处理前移除空的Authorization头,避免触发认证失败:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Value("${...}")
    private String user;
    @Value("${...}")
    private String pw;

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        UserDetails user = User
                .withUsername(user)
                .password(encoder.encode(pw))
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(user);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.addFilterBefore(removeEmptyAuthorizationHeaderFilter(), SecurityWebFiltersOrder.HTTP_BASIC)
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers("/api/rest/**").authenticated()
                        .anyExchange().permitAll()
                )
                .httpBasic(withDefaults());
        return http.build();
    }

    private ServerWebExchangeFilterFunction removeEmptyAuthorizationHeaderFilter() {
        return (exchange, chain) -> {
            HttpHeaders headers = exchange.getRequest().getHeaders();
            if (headers.containsKey(HttpHeaders.AUTHORIZATION)) {
                String authHeader = headers.getFirst(HttpHeaders.AUTHORIZATION);
                if (authHeader == null || authHeader.trim().isEmpty()) {
                    // 移除空的Authorization头
                    ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
                            .headers(h -> h.remove(HttpHeaders.AUTHORIZATION))
                            .build();
                    return chain.filter(exchange.mutate().request(modifiedRequest).build());
                }
            }
            return chain.filter(exchange);
        };
    }
}

内容的提问来源于stack exchange,提问作者jju-untis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 10:40:50