Подтвердить что ты не робот

Как использовать пользовательские выражения в Spring Security @PreAuthorize/@PostAuthorize аннотации

Есть ли способ создать более выразительные выражения в блоках @Preauthorize? Вот пример того, что я повторяю, потому что @Preauthorize не очень умный из коробки.

@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser) {
    Game currentGame = gameService.findById(id);
    if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) {
        gameService.delete(gameService.findById(id));
    } else {
        throw new SecurityException("Only an admin, or an owner can delete a game.");
    }
}

Я бы предпочел что-то вроде.

@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
@Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id)
public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser, @ModelAttribute currentGame ) { //I'm not sure how to add this either :(
   gameService.delete(gameService.findById(id));
}

Отчасти проблема заключается в том, что мне нужно сделать запрос к базе данных для получения некоторых из этих материалов для проверки разрешений, таких как запрос базы данных на получение копии игры, а затем сравнение владельца игры с лицо, делающее запрос. Я не совсем уверен, как все это работает в контексте обработчика аннотации @Preauthorize или как я добавляю вещи в коллекцию объектов, доступных в атрибуте значения @Preauthorize ("").

4b9b3361

Ответ 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(), возвращающий имя пользователя владельца.

Ответ 2

Так как @PreAuthorize оценивает SpEl -выражения, самый простой способ - просто указать на bean:

    @PreAuthorize("@mySecurityService.someFunction()")

MySecurityService.someFunction должен иметь тип возврата boolean.

Spring -security автоматически предоставит переменную с именем authentication, если вы хотите передать authentication -объект. Вы также можете использовать любые допустимые выражения SpEl для доступа к любым аргументам, переданным вашему безопасному методу, оценивать регулярные выражения, вызывать статические методы и т.д. Например:

    @PreAuthorize("@mySecurityService.someFunction(authentication, #someParam)")

Ответ 3

Вы можете написать свою аннотацию примерно так:

@PreAuthorize("hasRole('ROLE_ADMIN') and hasPermission(#id, 'Game', 'DELETE')")

Чтобы получить работу части hasPermission, вам необходимо реализовать PermissionEvaluator интерфейс.

Затем определите обработчик выражения bean:

@Autowired
private PermissionEvaluator permissionEvaluator;

@Bean
public DefaultMethodSecurityExpressionHandler expressionHandler()
{
    DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
    handler.setPermissionEvaluator(permissionEvaluator);
    return handler;
}

И введите в свою конфигурацию безопасности:

<global-method-security pre-post-annotations="enabled">
  <expression-handler ref="expressionHandler" />
</global-method-security>