Spring WebFlux 安全自定义 AccessDeniedHandler
Posted
技术标签:
【中文标题】Spring WebFlux 安全自定义 AccessDeniedHandler【英文标题】:Spring WebFlux Security Custom AccessDeniedHandler 【发布时间】:2019-03-29 12:48:44 【问题描述】:我有一个 REST 端点,访问权限仅限于 ADMIN 用户:
@GetMapping
@PreAuthorize("hasAuthority('ADMIN')")
fun getAll(): Flux<User>
return Flux.fromIterable(userRepository.findAll())
当我尝试使用非 ADMIN 用户访问此端点时,我收到带有 denied
响应(非 JSON 响应)的 403。
如何自定义响应以仍然收到 403 响应,但带有类似 JSON 的消息
"error": "Access denied"
我正在实施的SecurityWebFilterChain
:
return http.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.exceptionHandling().accessDeniedHandler exchange, denied -> ????
.and()
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers("/auth").permitAll()
.anyExchange().authenticated()
.and().csrf().disable()
.logout().disable()
.build()
【问题讨论】:
【参考方案1】:抱歉,我在 Kotlin 中没有这样做,但是在 Java 中你必须实现 org.springframework.security.web.server.authorization.ServerAccessDeniedHandler
接口:
public class CustomAccessDeniedHandler implements ServerAccessDeniedHandler
@Override
public Mono<Void> handle(ServerWebExchange serverWebExchange, AccessDeniedException accessDeniedException)
ServerHttpResponse response = serverWebExchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
String responseBody = "\"error\": \"" + accessDeniedException.getLocalizedMessage() + "\"";
byte[] bytes = responseBody.getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(buffer));
然后你可以将它传递给accessDeniedHandler()
方法:
return http.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())
.and()
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers("/auth").permitAll()
.anyExchange().authenticated()
.and().csrf().disable()
.logout().disable()
.build();
在 Postman 中测试我的端点时,我收到“403 Forbidden”错误,响应正文为:
"error": "Access Denied"
【讨论】:
【参考方案2】:您可以创建自定义过滤器并在收到 403 错误时修改响应。 像这样的:
class AuthorizationModifierFilter : Filter
....
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit
// Check whether the status code is 403 and modify the response
并注册您的过滤器:
return http.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.exceptionHandling().accessDeniedHandler exchange, denied -> ????
.and()
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers("/auth").permitAll()
.anyExchange().authenticated()
.and().addFilterAfter(AuthorizationModifierFilter(), UsernamePasswordAuthenticationFilter.java.class)
.and().csrf().disable()
.logout().disable()
.build()
【讨论】:
以上是关于Spring WebFlux 安全自定义 AccessDeniedHandler的主要内容,如果未能解决你的问题,请参考以下文章
spring5 webflux,如何返回自定义json数据?
在身份验证 Spring Security + WebFlux 期间抛出和处理自定义异常
为啥使用 webflux 进行 spring boot 测试会忽略自定义 jackson 模块