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 webflux 的自定义异常

在身份验证 Spring Security + WebFlux 期间抛出和处理自定义异常

为啥使用 webflux 进行 spring boot 测试会忽略自定义 jackson 模块

如何在 Spring WebFlux 上自定义未经授权的响应

缺少资源 bean:Spring Boot webflux 自定义全局异常处理程序