问题背景:
springboot版本:3.2.4,项目中集成了springSecurity和webflux
问题表现为:正常同步请求没问题,但一使用webflux的数据结构(Mono、Flux)返回就报错。java代码:
@Operation(summary = "同步请求3")
@PostMapping(value = "/syn3")
@ApiOperationSupport(order = 4)
public Mono<DemoEntity> sync() {
return Mono.create(x -> x.success(new DemoEntity()
.setId(11L)
.setName("张三")
.setSexName("男")));
}
报错信息:
org.springframework.security.authentication.InsufficientAuthenticationException: Full authentication is required to access this resource
at org.springframework.security.web.access.ExceptionTranslationFilter.handleAccessDeniedException(ExceptionTranslationFilter.java:199)
at org.springframework.security.web.access.ExceptionTranslationFilter.handleSpringSecurityException(ExceptionTranslationFilter.java:178)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:147)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
......
排查过程:springSecurity的token解析,我放在了UsernamePasswordAuthenticationFilter之前,提前放进了SecurityContextHolder。经跟踪发现请求到方法里,再向前端返回时,又验证了一次权限,而在响应前SecurityContextHolder已经执行了一次clearContext清除了之前放的token信息。试了很多方案,最终在在懈努力下官网的找到了一丝线索:AuthorizationFilter,
最终解决方案出炉,相当简单:加配置让authorizeHttpRequests中的异步不再验证异步类型
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.authorizeHttpRequests(req -> req.requestMatchers(whiteList).permitAll()
// webflux异步响应,不再二次验证权限
.dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
.anyRequest().authenticated())
.exceptionHandling(http -> http.authenticationEntryPoint(failureHandling()))
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.build();
}
对于部分低版本springboot(2.x及以下)的配置是:
httpSecurity.authorizeRequests().antMatchers(UserConstants.WHITE_LIST).permitAll()
// webflux异步响应,不再二次验证权限
.filterSecurityInterceptorOncePerRequest(false)
.anyRequest().authenticated()
.and().csrf().disable()
.cors().disable()
// ....