几天前,在使用Grails应用程序时,我处于一种需要从Spring安全SPEL表达式调用bean方法的情况。 我在使用Spring Security ACL插件中的@PreAuthorize批注,并想要执行以下操作:
@PreAuthorize("myService.canAccessUserProfile(#profileId)")
public Profile getUserProfile(long profileId) {
...
}
@PreAuthorize将SPEL表达式作为参数,对它进行评估以查看是否允许当前登录的用户访问getUserProfile()方法。 在此SPEL表达式中,我想调用myService的canAccessUserProfile()方法来执行安全检查。 在下面的内容中,我将说明执行此步骤所需的步骤。
幸运的是,可以通过在SPEL表达式中使用@符号作为前缀来引用bean:
@PreAuthorize("@myService.canAccessUserProfile(#profileId)")
但是,仅凭此更改是无法立即使用的,我们必须对Spring Security配置进行一些小的调整。
用于解析安全性表达式的SPEL表达式解析器(请参阅: SpelExpressionParser )将Bean的查找委托给BeanResolver 。 为了使上述安全性表达式起作用,我们必须创建一个BeanResolver实现并将其添加到Spring Security配置中。
为Grails创建BeanResolver非常简单:
class GrailsBeanResolver implements BeanResolver {
GrailsApplication grailsApplication
@Override
public Object resolve(EvaluationContext evaluationContext, String beanName) throws AccessException {
return grailsApplication.mainContext.getBean(beanName)
}
}
我们只需要实现resolve()方法来满足BeanResolver接口。 在我们的示例中,我们将此工作委托给Grails应用程序的bean工厂。 因此,我们可以访问安全表达式中的所有可用bean。
现在,我们必须将bean解析器添加到SPEL评估上下文中(请参阅: EvaluationContext )。 这可以通过重写Spring Securities DefaultMethodSecurityExpressionHandler的createEvaluationContext()来完成:
class GrailsExpressionHandler extends DefaultMethodSecurityExpressionHandler {
BeanResolver beanResolver
@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation method) {
StandardEvaluationContext context = (StandardEvaluationContext) super.createEvaluationContext(auth, method)
context.setBeanResolver(beanResolver)
return context;
}
}
顾名思义,createEvaluationContext()负责为安全性表达式创建评估上下文。 我们唯一要做的就是在创建EvaluationContext之后添加beanResovler。
之后,我们必须使用Spring Bean DSL在grails-app / conf / spring / resources.groovy中配置我们的两个新bean:
beans = {
expressionHandler(GrailsExpressionHandler) {
beanResolver = ref('beanResolver')
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('permissionEvaluator')
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
}
beanResolver(GrailsBeanResolver) {
grailsApplication = ref('grailsApplication')
}
}
通常,expressionHandler bean将是如上所述的DefaultMethodSecurityExpressionHandler的实例。 因为我们希望Spring Security使用GrailsExpressionHandler,所以我们必须重写expressionHandler bean。 我们添加的唯一新依赖项是beanResolver属性。 DefaultMethodSecurityExpressionHandler(GrailsExpressionHandler的基类)需要expressionHandler的其他四个依赖项。 这些依赖关系已经由Spring Security插件提供。
现在应该可以使用@前缀引用bean并在安全性表达式中调用它们的方法。