【问题标题】:Spring WebFlux Security Custom AccessDeniedHandlerSpring WebFlux 安全自定义 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()

【问题讨论】:

    标签: spring spring-security kotlin spring-webflux


    【解决方案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()
      

      【讨论】:

        猜你喜欢
        • 2015-09-13
        • 2011-02-10
        • 1970-01-01
        • 2020-11-18
        • 2020-10-09
        • 2019-08-05
        • 2011-05-04
        • 2020-08-31
        • 2014-10-18
        相关资源
        最近更新 更多