Как создать пользовательские методы для использования в spring аннотациях выражения выражения безопасности
Я хотел бы создать класс, который добавляет пользовательские методы для использования в spring языке выражения безопасности для авторизации на основе методов через аннотации.
Например, я хотел бы создать собственный метод, например customMethodReturningBoolean, который будет использоваться так:
@PreAuthorize("customMethodReturningBoolean()")
public void myMethodToSecure() {
// whatever
}
Мой вопрос в том, что.
Если возможно, какой класс должен быть подклассом для создания моих пользовательских методов, как бы я решил настроить его в конфигурационных файлах spring xml и пришел ли кто-нибудь, чтобы я привел пример пользовательского метода, используемого таким образом?
Ответы
Ответ 1
Вам нужно подклассифицировать два класса.
Сначала установите новый обработчик выражения метода
<global-method-security>
<expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>
myMethodSecurityExpressionHandler
будет подклассом DefaultMethodSecurityExpressionHandler
, который переопределяет createEvaluationContext()
, устанавливая подкласс MethodSecurityExpressionRoot
на MethodSecurityEvaluationContext
.
Например:
@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
root.setTrustResolver(trustResolver);
root.setPermissionEvaluator(permissionEvaluator);
root.setRoleHierarchy(roleHierarchy);
ctx.setRootObject(root);
return ctx;
}
Ответ 2
Ни один из указанных методов не будет работать больше. Кажется, что Spring прошел большую длину, чтобы пользователи не переопределили SecurityExpressionRoot.
EDIT 11/19/14 Настройка Spring для использования аннотаций безопасности:
<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... >
...
<sec:global-method-security pre-post-annotations="enabled" />
Создайте bean следующим образом:
@Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(String key) {
return true;
}
}
Затем сделайте что-то подобное в jsp:
<sec:authorize access="@mySecurityService.hasPermission('special')">
<input type="button" value="Special Button" />
</sec:authorize>
Или аннотировать метод:
@PreAuthorize("@mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }
И помните: если вы используете Spring, и вам нужно решить проблему, расширив классы, переопределяя методы, внедряя интерфейсы и т.д.... тогда вы, вероятно, делаете что-то неправильно. Все аннотации и xml, поэтому мы очень любим Spring и не (старые версии) EJB.
Кроме того, вы можете использовать Spring Язык выражения в своих аннотациях @PreAuthorize
для доступа к текущей аутентификации, а также к аргументам метода.
Например:
@Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(Authentication authentication, String foo) { ... }
}
Затем обновите свой @PreAuthorize
, чтобы он соответствовал новой сигнатуре метода:
@PreAuthorize("@mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }
Ответ 3
Спасибо ericacm, но это не работает по нескольким причинам:
- Свойства DefaultMethodSecurityExpressionHandler являются закрытыми (нежелательные клики видимости)
- По крайней мере, в моем Eclipse я не могу разрешить объект MethodSecurityEvaluationContext
Различия заключаются в том, что мы называем существующий метод createEvaluationContext, а затем добавляем наш пользовательский корневой объект. Наконец, я просто вернул тип объекта StandardEvaluationContext, поскольку метод MethodSecurityEvaluationContext не будет разрешен в компиляторе (они оба из одного и того же интерфейса). Это код, который я сейчас имею в производстве.
Сделать MethodSecurityExpressionHandler использовать наш пользовательский корень:
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
// parent constructor
public CustomMethodSecurityExpressionHandler() {
super();
}
/**
* Custom override to use {@link CustomSecurityExpressionRoot}
*
* Uses a {@link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
* configures it with a {@link MethodSecurityExpressionRoot} instance as the expression root object.
*/
@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
// due to private methods, call original method, then override it root with ours
StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
return ctx;
}
}
Это заменяет корень по умолчанию, расширяя SecurityExpressionRoot. Здесь я переименовал hasRole в hasEntitlement:
public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {
// parent constructor
public CustomSecurityExpressionRoot(Authentication a) {
super(a);
}
/**
* Pass through to hasRole preserving Entitlement method naming convention
* @param expression
* @return boolean
*/
public boolean hasEntitlement(String expression) {
return hasRole(expression);
}
}
Наконец, обновите securityContext.xml(и убедитесь, что он ссылается на ваш applcationContext.xml):
<!-- setup method level security using annotations -->
<security:global-method-security
jsr250-annotations="disabled"
secured-annotations="disabled"
pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<!--<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">-->
<bean id="expressionHandler" class="com.yourSite.security.CustomMethodSecurityExpressionHandler" />
Примечание. аннотация @Secured не принимает это переопределение при прохождении через другой обработчик проверки. Итак, в приведенном выше xml я отключил их, чтобы предотвратить путаницу.