如何在spring应用程序中集成abac?

sh7euo9m  于 2021-09-30  发布在  Java
关注(0)|答案(1)|浏览(425)

SpringSecurity提供了类似于注解的授权方法( @PreAuthorize )和安全特定的spel表达式,如 hasRole . 原则上,这种表达式语言可以扩展以支持abac,但这并不能真正解决在何处执行的体系结构问题。
应用程序体系结构的灵感来源于clean体系结构(clean体系结构的灵感来源于Hexagon体系结构和onion体系结构)。基本构建块如下所示:
适配器:连接到外部世界,例如。Ghttp端点
应用程序服务:与DTO之间的转换、提供事务、执行授权
域:描述业务数据、规则和行为
在我看来,应用层是唯一适合执行授权的层。它不适合适配器层,因为一个应用程序服务可能有多个适配器,因此必须在多个地方重复授权。它显然不适合这个领域,因为它是一个技术方面,不应该成为领域逻辑的一部分。
典型的应用程序服务方法从客户端获取一些或多或少的“原始”参数,将其转换并将执行委托给域:

@Transactional
class MyApplicationService {
    fun cancelOrder(id: Long) {
        val orderId = OrderId(orderId)
        val order = orderRepository.findById(orderId)
        order.cancel()
    }
}

为了能够执行基于属性的访问控制(abac),必须先加载order对象,以便首先提取属性。这也意味着像 @PreAuthorize 高于 cancelOrder 函数将无法工作,因为order对象在那里不可用(spel将能够引用函数参数,但order不会作为参数传递!)。
我能想到的唯一剩下的可能性是在继续进行业务逻辑之前显式调用授权函数:

fun cancelOrder(id: Long, principal: Principal) {
        val orderId = OrderId(id)
        val order = orderRepository.findById(orderId)
        checkPolicy(principal, order, Context.Complaint) // ?
        order.cancel()
    }

具体的abac服务器(如opa、authzforce等)尚未确定,但这一级别的解决方案应该独立于它。
对于我概述的内容,有没有一种通用的方法 checkPolicy ? 是否有一种典型的“spring方法”来整合外部政策决策点?还是有更好的根本解决方案?

iqjalb3h

iqjalb3h1#

选项1-如果可以修改订单类别:
你可以申请 @PreAuthorizeOrder#cancel() 方法,这样您就可以像 checkPolicy 并访问 order 反对 #this ):

class Order {
 //...
 @PreAuthorize("hasPermission(#this, T(fully.qualified.Context).Complaint)")
 fun cancel() {...}
}

如果你的 Context.Complaint 常量是一个字符串,可能它更易于读取,以放置字符串值(例如。 'complaint' ?)而不是 T(...Context).Complaint 在上面的spel表达式中。
选项2-如果无法修改订单类别:
您可以使用代理模式,即将调用 Package 到 order.cancel 在中间类/函数中,并在其上添加注解:

@Transactional
class MyApplicationService {
 fun cancelOrder(id: Long, principal: Principal) {
  val orderId = OrderId(id)
  // Proxy pattern - new intermediary class 
  val order = OrderSecProxy(orderRepository.findById(orderId))
  order.cancel()
 }
}

class OrderSecProxy(val order: Order) {
 @PreAuthorize("hasPermission(#this.order, T(fully.qualified.Context).Complaint)")
 fun cancel() {
  order.cancel()
 }
}

然后,您必须实现自定义permissionevaluator(spring security):

class AbacPermissionEvaluator implements PermissionEvaluator {

    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
     log.info("check permission '{}' for user '{}' for target '{}'", permission, authentication.getPrincipal(), targetDomainObject)
     var principal = authentication.getPrincipal()
     // Make the authorization request to the PDP with subject attributes from principal, action attribute from permission and resource attributes from targetDomainObject
     // ...
     // return true if PDP's decision is Permit, else false.
    }
}

更多信息。
所以你应该得到 order 来自 targetDomainObject 论点但是,通过更改 @PreAuthorize 表达方式如下:
我假设order类至少有一些 id 属性,但可以有其他属性。
在选项1的情况下:

@PreAuthorize("hasPermission(T(java.util.Map).of('resource-id', #this.id), T(fully.qualified.Context).Complaint)")

在选择2的情况下:

@PreAuthorize("hasPermission(T(java.util.Map).of('resource-id', #this.order.id), T(fully.qualified.Context).Complaint)")

最后,在应用程序上下文中配置permissionevaluator:

<bean id="abacPermEvaluator" class="fully.qualified.AbacPermissionEvaluator">
    <!-- properties -->
</bean>

<security:global-method-security pre-post-annotations="enabled">
 <security:expression-handler>
  <bean class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator" ref="abacPermEvaluator"/>
    <!-- other properties like permissionCacheOptimizer-->
  </bean>
 </security:expression-handler>
</security:global-method-security>

您可以像往常一样对注解进行相同的配置。

相关问题