SpringBoot Webflux如何忽略带空Authorization头的请求
当前配置通过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
相关产品推荐
相关产品推荐

