96 votes

Comment créer des méthodes personnalisées à utiliser dans les annotations du langage d'expression de la sécurité de Spring ?

Je voudrais créer une classe qui ajoute des méthodes personnalisées à utiliser dans le langage d'expression de sécurité de Spring pour une autorisation basée sur les méthodes via des annotations.

Par exemple, je voudrais créer une méthode personnalisée comme "customMethodReturningBoolean" à utiliser d'une manière ou d'une autre comme ceci :

  @PreAuthorize("customMethodReturningBoolean()")
  public void myMethodToSecure() { 
    // whatever
  }

Ma question est la suivante. Si c'est possible, quelle classe dois-je sous-classer pour créer mes méthodes personnalisées, comment dois-je procéder pour les configurer dans les fichiers de configuration de Spring xml et quelqu'un peut-il me donner un exemple de méthode personnalisée utilisée de cette façon ?

2 votes

Je n'ai pas le temps de taper une réponse pour le moment, mais j'ai suivi ce guide et cela a fonctionné à merveille : baeldung.com/ J'utilise Spring Security 5.1.1.

185voto

James Watkins Points 595

Aucune des techniques mentionnées ne fonctionnera plus. Il semble que Spring se soit donné beaucoup de mal pour empêcher les utilisateurs de surcharger le SecurityExpressionRoot.

EDIT 11/19/14 Configurer Spring pour utiliser les annotations de sécurité :

<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... >
...
<sec:global-method-security pre-post-annotations="enabled" />

Créez un haricot comme ceci :

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(String key) {
        return true;
    }
}

Puis faites quelque chose comme ceci dans votre jsp :

<sec:authorize access="@mySecurityService.hasPermission('special')">
    <input type="button" value="Special Button" />
</sec:authorize>

Ou annoter une méthode :

@PreAuthorize("@mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }

En outre, vous pouvez utiliser Langage d'expression du printemps dans votre @PreAuthorize pour accéder à l'authentification actuelle ainsi qu'aux arguments de la méthode.

Par exemple :

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(Authentication authentication, String foo) { ... }
}

Ensuite, mettez à jour votre @PreAuthorize pour correspondre à la nouvelle signature de la méthode :

@PreAuthorize("@mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }

0 votes

Il y a un moyen de donner à MySecurityService un accès à un service existant. Authentication objet ?

6 votes

@Bosh dans votre méthode hasPermission, vous pouvez utiliser Authentication auth = SecurityContextHolder.getContext().getAuthentication(); pour obtenir le jeton d'authentification actuel.

2 votes

Merci James pour votre réponse. Dois-je définir mySecurityService dans le fichier de configuration de Spring ?

35voto

sourcedelica Points 12533

Vous aurez besoin de sous-classer deux classes.

Tout d'abord, définissez un nouveau gestionnaire d'expression de méthode

<global-method-security>
  <expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>

myMethodSecurityExpressionHandler sera une sous-classe de DefaultMethodSecurityExpressionHandler qui prévaut sur createEvaluationContext() qui définit une sous-classe de MethodSecurityExpressionRoot sur le MethodSecurityEvaluationContext .

Par exemple :

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

0 votes

Hmm, ça semble être une bonne idée, mais toutes les propriétés de DefaultMethodSecurityExpressionHandler sont privés sans accesseurs, donc j'étais curieux de savoir comment vous avez étendu la classe sans aucune réflexion désagréable. Merci.

1 votes

Vous voulez dire trustResolver, etc ? Ils ont tous des setters dans DefaultMethodSecurityExpressionHandler (au moins dans Spring Security 3.0). Voir : static.springsource.org/spring-security/site/apidocs/org/

3 votes

@ericacm Comment vous déplacez-vous MethodSecurityExpressionRoot être paquet-privé ?

14voto

Joseph Lust Points 4961

Merci ericacm mais cela ne fonctionne pas pour plusieurs raisons :

  • Les propriétés de DefaultMethodSecurityExpressionHandler sont privées (les kludges de visibilité de réflexion sont indésirables)
  • Au moins dans mon Eclipse, je ne peux pas résoudre un MethodSecurityEvaluationContext objet

Les différences sont que nous appelons l'existant createEvaluationContext et ensuite ajouter notre objet Root personnalisé. Enfin, je viens de renvoyer un Contexte d'évaluation standard puisque MethodSecurityEvaluationContext ne serait pas résolu dans le compilateur (ils sont tous deux issus de la même interface). Voici le code que j'ai maintenant en production.

Faire MethodSecurityExpressionHandler utiliser notre racine personnalisée :

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's root with ours
        StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
        ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
        return ctx;
    }
}

Cela remplace la racine par défaut en étendant SecurityExpressionRoot . Ici, j'ai renommé hasRole en 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);
    }

}

Enfin, mettez à jour securityContext.xml (et assurez-vous qu'il est référencé à partir de votre 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" />

Nota: l'annotation @Secured n'acceptera pas ce remplacement car elle passe par un gestionnaire de validation différent. Donc, dans le xml ci-dessus, je les ai désactivés pour éviter toute confusion ultérieure.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X