我正在开发一个必须进行对象级安全检查的应用程序,并且检查将由服务进行,因为它需要对单独的应用程序进行REST调用。因此,我无法使用Spring Security角色或ACL,因为这些信息都不会存储在应用程序的本地。我试图找到一种优雅的方式来处理这个问题,这里有两个我能想到的选择:
1)创建一个将检查权限的自定义注释
2)扩展一个Spring安全注释权限检查(可能是Permission Evaluator?),它允许我编写检查访问的逻辑
对于#1我已经创建了一个自定义注释并使用过滤器来读取注释并检查访问权限,虽然这看起来更脆弱,只会给我控制器操作的保护,这样会很好也可以保护其他服务。
我发现了一些信息,但没有完整。
THIS讨论了自定义ACL,但仅针对新权限,而不是控制逻辑
THIS谈到使用SpEL,但我希望在方法运行之前进行检查,以确保不会发生任何未经授权的效果。
THIS似乎与我想做的最接近,但是特定于Spring Security而不是Grails - 我最大的挑战是将applicationContext.xml中的信息转换为resources.groovy
提前感谢您提出的任何建议或建议!
答案 0 :(得分:10)
你应该能够在没有太多麻烦的情况下使用弹簧安全和grails来做到这一点。
我过去使用以下两种方法来完成类似的任务。两者都需要弹簧安全ACL插件,该插件提供@PreAuthorize
和@PostAuthorize
注释。
自定义PermissionEvaluator
您可以在安全注释中使用hasPermission()
方法并创建自定义PermissionEvaluator
。在代码中,这看起来像这样:
@PreAuthorize("hasPermission(#myObject, 'update')")
public void updateSomething(myObject) {
..
}
hasPermission()
次呼叫通过Spring安全性路由到PermissionEvaluator
。要编写自己的实现,必须实现PermissionEvaluator
接口:
class MyPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
// your custom logic..
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
// your custom logic
}
}
要注册PermissionEvaluator
,您必须覆盖名为expressionHandler
的bean。您可以通过在conf/spring/resources.groovy
中添加以下行来执行此操作:
beans = {
expressionHandler(MyExpressionHandler) {
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('myPermissionEvaluator') // your PermissionEvaluator
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
}
myPermissionEvaluator(MyPermissionEvaluator)
}
在resources.groovy
中,您可以像使用spring时在applicationContext.xml
中一样定义bean。上面的行创建了一个类型为MyPermissionEvaluator
的bean,其bean名称为myPermissionEvaluator
。使用类型为expressionHandler
的bean重写Spring security MyExpressionHandler
bean。其他依赖项从spring安全ACL插件的配置文件中复制。
安全注释中的服务调用
如果hasPermission()
方法的设计无法满足您的所有要求,则可以使用简单的服务调用。 @PostAuthorize
和@PreAuthorize
注释使用SPEL来评估表达式。在SPEL中,您可以使用@
符号来访问bean。例如:
@PreAuthorize("@securityService.canAccess(#myObject)")
public void doSomething(myObject) {
..
}
这将调用名为canAccess
的bean的securityService
方法,并将方法参数传递给它。
要使用此方法,您必须在BeanResolver上注册EvaluationContext。为此,您必须覆盖由spring security ACL插件配置的DefaultMethodSecurityExpressionHandler。
这可能如下所示:
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
BeanResolver beanResolver
@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi)
ctx.setBeanResolver(beanResolver) // set BeanResolver here
return ctx;
}
}
BeanResolver
是一个简单的接口,它将bean名称解析为bean实例:
class GrailsBeanResolver implements BeanResolver {
GrailsApplication grailsApplication
@Override
public Object resolve(EvaluationContext evaluationContext, String beanName) throws AccessException {
return grailsApplication.mainContext.getBean(beanName)
}
}
最后将豆子添加到resources.groovy
:
expressionHandler(MyExpressionHandler) {
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('permissionEvaluator')
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
beanResolver = ref('beanResolver') // this is your BeanResolver
}
// This is the service called within security expressions
// If you place your service in the grails service folder you can skip this line
securityService(MySecurityService)
// this is your BeanResolver
beanResolver(GrailsBeanResolver) {
grailsApplication = ref('grailsApplication')
}
更新(2013-10-22):最近我写了一篇blog post,其中提供了一些额外的信息。