Spring Reactive Security |如何实现反应式PermissionEvaluator

时间:2018-05-20 09:32:20

标签: spring-security spring-webflux

假设我们有一个带注释的休息控制器方法:

@PreAuthorize("hasPermission(#username, 'USER_PROFILE', 'WRITE')")

在Spring MVC中,我们将实现PermissionEvaluator来实现隐藏在以下方法签名背后的授权

boolean hasPermission(
  Authentication authentication, 
  Serializable targetId, 
  String targetType,
  Object permission
)

只要你不需要在hasPermission方法中调用一个被动服务/方法,这似乎仍然可以使用Spring WebFlux,我想很少这样,因为你通常喜欢使用被动接口您的数据库层也是如此。如果您在此方法中调用被动服务,则需要在某个时间点block()调用Mono,从而因为从被动管道中调用而遇到麻烦。

Josh Long关于Spring WebFlux安全性的教程中,他解释了如何使用路径匹配器直接在SecurityWebFilterchain上通过提供自定义ReactiveAuthorizationManager来实现授权。但是没有解释如何在Spring WebFlux中使用PreAuthorize注释。

我原本希望实施一些ReactivePermissionEvaluator

Mono<Boolean> hasPermission(
  Authentication authentication, 
  Serializable targetId, 
  String targetType,
  Object permission
)

允许在实现中使用被动服务但我无法找到任何可以扫描ReactiveAuthorizationManager注释的PreAuthorize实现并将evalutation分配给被动{ {1}} PermissionEvaluator接口也不存在。

所以最后问题是,如何实现允许调用被动服务的被动ReactivePermissionEvaluator,例如查询数据库中的autorization信息而不阻塞?

2 个答案:

答案 0 :(得分:0)

遗憾的是,https://github.com/spring-projects/spring-security/issues/5046的Spring Security尚不支持此功能

答案 1 :(得分:0)

这是你应该怎么做。只需从 applicationContext 获取 DefaultMethodSecurityExpressionHandler bean 并显式替换 permissionsEvaluator。

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfiguration {
@Autowire
private ApplicationContext applicationContext;

    @Bean
    @DependsOn({"methodSecurityExpressionHandler"})
    public SecurityWebFilterChain springSecurityFilterChain(
            ServerHttpSecurity http) {
        DefaultMethodSecurityExpressionHandler defaultWebSecurityExpressionHandler = this.applicationContext.getBean(DefaultMethodSecurityExpressionHandler.class);
        defaultWebSecurityExpressionHandler.setPermissionEvaluator(permissionEvaluator());
        return http.csrf().disable()
                .httpBasic().disable()
                .formLogin().disable()
                .logout().disable()
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
                .addFilterAt(tokenAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
                .authorizeExchange()
                .anyExchange().authenticated()
                .and().build();

    }

   PermissionEvaluator permissionEvaluator() {
    return new PermissionEvaluator() {
        @Override
        public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
            //Custom logic to evaluate @PreAuthorize("hasPermission('123', '123')")
            return false;
        }

        @Override
        public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
            //Custom logic to evaluate @PreAuthorize("hasPermission('123', '123','123')")
            return false;
        }
    };
}