Несколько дней назад, работая над приложением Grails, я попал в ситуацию, когда хотел вызвать метод bean-компонента из выражения SPEL безопасности Spring. Я использовал аннотацию @PreAuthorize из плагина Spring Security ACL и хотел сделать что-то вроде этого:
1
2
3
4
|
@PreAuthorize ( "myService.canAccessUserProfile(#profileId)" ) public Profile getUserProfile( long profileId) { ... } |
@PreAuthorize принимает выражение SPEL в качестве параметра, который оценивается, чтобы увидеть, разрешено ли текущему вошедшему в систему пользователю доступ к методу getUserProfile (). В этом выражении SPEL я хотел вызвать метод canAccessUserProfile () myService для выполнения проверки безопасности. Далее я объясню, какие шаги необходимы, чтобы это работало.
К счастью, можно ссылаться на bean-компоненты из выражений SPEL, поставив перед их именем символ @:
1
|
@PreAuthorize ( "@myService.canAccessUserProfile(#profileId)" ) |
Однако одно это изменение не будет работать сразу после установки, и нам придется внести некоторые небольшие изменения в конфигурацию Spring Security.
Анализатор выражений SPEL (см .: SpelExpressionParser ), который используется для анализа выражения безопасности, делегирует поиск bean-компонентов BeanResolver . Для того чтобы приведенное выше выражение безопасности работало, нам нужно создать реализацию BeanResolver и добавить ее в конфигурацию Spring Security.
Создать BeanResolver для Grails очень просто:
1
2
3
4
5
6
7
8
9
|
class GrailsBeanResolver implements BeanResolver { GrailsApplication grailsApplication @Override public Object resolve(EvaluationContext evaluationContext, String beanName) throws AccessException { return grailsApplication.mainContext.getBean(beanName) } } |
Нам нужно только реализовать метод resol () для удовлетворения интерфейса BeanResolver. В нашем примере мы делегируем эту работу фабрике бинов нашего приложения Grails. Таким образом, мы можем получить доступ ко всем доступным компонентам в выражениях безопасности.
Теперь нам нужно добавить наш преобразователь компонентов в контекст оценки SPEL (см. EvaluationContext ). Это может быть сделано путем переопределения createEvaluationContext () Spring Securities DefaultMethodSecurityExpressionHandler:
01
02
03
04
05
06
07
08
09
10
11
|
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 () отвечает за создание контекста оценки для выражений безопасности. Единственное, что мы делаем, это добавляем beanResovler после создания EvaluationContext.
После этого нам нужно сконфигурировать два наших новых bean-компонента в grails-app / conf / spring / resources.groovy с использованием Spring Bean DSL:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
beans = { expressionHandler(GrailsExpressionHandler) { beanResolver = ref( 'beanResolver' ) parameterNameDiscoverer = ref( 'parameterNameDiscoverer' ) permissionEvaluator = ref( 'permissionEvaluator' ) roleHierarchy = ref( 'roleHierarchy' ) trustResolver = ref( 'authenticationTrustResolver' ) } beanResolver(GrailsBeanResolver) { grailsApplication = ref( 'grailsApplication' ) } } |
Обычно bean-компонент expressionHandler является экземпляром DefaultMethodSecurityExpressionHandler, как упомянуто выше. Поскольку мы хотим, чтобы Spring Security использовала наш GrailsExpressionHandler, мы должны переопределить bean-компонент expressionHandler. Единственной новой зависимостью, которую мы добавили, является свойство beanResolver. Другие четыре зависимости expressionHandler требуются для DefaultMethodSecurityExpressionHandler (базовый класс GrailsExpressionHandler). Эти зависимости уже предоставлены плагинами Spring Security.
Теперь должна быть возможность ссылаться на bean-компоненты с помощью префикса @ и вызывать их методы в выражениях безопасности.