Ответ 1
1) Сначала вам нужно переопределить MethodSecurityExpressionRoot
, который содержит дополнительные функции, специфичные для конкретного метода. Первоначальная реализация безопасности Spring является закрытой для пакета, и, следовательно, ее невозможно просто расширить. Я предлагаю проверить исходный код для данного класса.
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
// copy everything from the original Spring Security MethodSecurityExpressionRoot
// add your custom methods
public boolean isAdmin() {
// do whatever you need to do, e.g. delegate to other components
// hint: you can here directly access Authentication object
// via inherited authentication field
}
public boolean isOwner(Long id) {
// do whatever you need to do, e.g. delegate to other components
}
}
2) Затем вы должны реализовать пользовательский MethodSecurityExpressionHandler
, который будет использовать вышеуказанный CustomMethodSecurityExpressionRoot
.
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
@Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
}
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
3) Определите обработчик выражения bean в вашем контексте, например. через XML вы можете сделать это следующим образом
<bean id="methodSecurityExpressionHandler"
class="my.package.CustomMethodSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy" />
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
4) Зарегистрируйте указанный выше обработчик
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>
5) Затем просто используйте определенные выражения в ваших аннотациях @PreAuthorize
и/или @PostAuthorize
@PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) {
// do whatever needed
}
И еще одно. Не очень часто использовать защиту уровня метода для защиты методов контроллера, а скорее для защиты методов с помощью бизнес-логики (a.k.a. ваши методы уровня обслуживания). Тогда вы можете использовать что-то вроде ниже.
public interface GameService {
// rest omitted
@PreAuthorize("principal.admin or #game.owner = principal.username")
public void delete(@P("game") Game game);
}
Но имейте в виду, что это всего лишь пример. Он ожидает, что фактический директор имеет метод isAdmin()
и что в игре есть метод getOwner()
, возвращающий имя пользователя владельца.