Comment utiliser des expressions personnalisées dans la sécurité du printemps @Préauthorize/@PostAuthorize annotations

y a-t-il un moyen de créer des déclarations plus expressives dans les blocs @Preauthorizate? Voici un exemple de quelque chose que je me retrouve à répéter, parce que le @Preauthorizate n'est pas très intelligent hors de la boîte.

@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.");
    }
}

ce que je préférerais c'est quelque chose comme.

@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));
}

une partie du problème est que j'ai besoin de faire une requête à la base de données pour récupérer certains de ces trucs pour vérifier les permissions, comme questionner la base de données pour obtenir une copie du jeu, et puis comparer les propriétaire du jeu à la personne qui fait la demande. Je ne suis pas vraiment sûr de la façon dont tout cela fonctionne dans le contexte d'un processeur d'annotation @Preauthorizate, ou comment j'ajoute des choses à la collection d'objets mis à disposition dans l'attribut de valeur @Preauthorizate ("").

23
demandé sur pgiecek 2014-11-05 03:06:07

3 réponses

1) vous devez d'abord réimposer MethodSecurityExpressionRoot qui contient des fonctionnalités supplémentaires spécifiques à la méthode. L'implémentation originale de la sécurité du ressort est package private Et il n'est donc pas possible de l'étendre. Je suggère de vérifier le code source pour la classe donnée.

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) Ensuite vous devez implémenter custom MethodSecurityExpressionHandler qui utilisera leCustomMethodSecurityExpressionRoot.

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) Définissez l'expression handler bean dans votre contexte, par exemple via XML vous pouvez le faire comme suit

<bean id="methodSecurityExpressionHandler"
    class="my.package.CustomMethodSecurityExpressionHandler">
    <property name="roleHierarchy" ref="roleHierarchy" />
    <property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>

4) Enregistrer le handler défini ci-dessus

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

5) alors utilisez juste les expressions définies dans votre @PreAuthorize et/ou @PostAuthorize annotations

@PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) {
    // do whatever needed
}

et encore une chose. Il n'est pas très courant d'utiliser la sécurité au niveau de la méthode pour sécuriser les méthodes du contrôleur, mais plutôt pour sécuriser les méthodes avec une logique opérationnelle (A. K. A. vos méthodes de la couche de service). Alors vous pourriez utiliser quelque chose comme le dessous.

public interface GameService {

    // rest omitted

    @PreAuthorize("principal.admin or #game.owner = principal.username")
    public void delete(@P("game") Game game);
}

Mais gardez à l'esprit que c'est juste un exemple. Il s'attend à ce que le directeur actuel ait isAdmin() méthode et que le jeu a getOwner() méthode de retour du nom d'utilisateur du propriétaire.

23
répondu pgiecek 2014-11-05 13:29:43

Depuis @PreAuthorize évalue SpEl-expressions, de la façon la plus simple est juste à point pour un bean:

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

MySecurityService.someFunction devrait avoir le type de retour boolean.

Spring-security fournira automatiquement une variable nommée authentication si vous voulez passer le Authentication-objet. Vous pouvez également utiliser toutes les expressions SpEl valides pour accéder à tous les arguments passés à votre méthode sécurisée, évaluer les expressions régulières, appeler des méthodes statiques, etc. E. g:

    @PreAuthorize("@mySecurityService.someFunction(authentication, #someParam)")
43
répondu gogstad 2016-10-11 07:31:38

vous pourriez écrire votre annotation quelque chose comme:

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

pour que la partie hasPermission fonctionne, vous devez implémenter PermissionEvaluator interface.

puis définir une expression handler bean:

@Autowired
private PermissionEvaluator permissionEvaluator;

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

Et l'injecter dans la sécurité de votre config:

<global-method-security pre-post-annotations="enabled">
  <expression-handler ref="expressionHandler" />
</global-method-security>
9
répondu holmis83 2014-11-05 10:39:02