web-dev-qa-db-fra.com

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

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

Par exemple, je voudrais créer une méthode personnalisée telle que 'customMethodReturningBoolean' à utiliser comme ceci:

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

Ma question est la suivante. Si cela est possible, quelle classe dois-je sous-classer pour créer mes méthodes personnalisées, comment puis-je configurer les méthodes dans les fichiers de configuration Spring xml et que quelqu'un vienne me donner un exemple de méthode personnalisée utilisée de cette manière?

87
Paul D. Eden

Vous devrez 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 remplacera createEvaluationContext(), définissant 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;
}
34
sourcedelica

Aucune des techniques mentionnées ne fonctionnera plus. Il semble que Spring ait fait beaucoup d'efforts pour empêcher les utilisateurs de remplacer le SecurityExpressionRoot.

EDIT 19/11/14 Setup Spring à 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;
    }
}

Ensuite, faites quelque chose comme ça 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() { ... }

De plus, vous pouvez utiliser Spring Expression Language dans votre @PreAuthorize _ annotations pour accéder à l’authentification actuelle ainsi que les arguments de la méthode.

Par exemple:

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

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

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

Merci ericacm , mais cela ne fonctionne pas pour quelques raisons:

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

Les différences sont que nous appelons la méthode existante createEvaluationContext , puis ajoutons notre objet racine personnalisé. Enfin, je viens de renvoyer un type d'objet StandardEvaluationContext puisque MethodSecurityEvaluationContext ne serait pas résolu dans le compilateur (ils proviennent tous deux de la même interface). C'est le code que j'ai maintenant en production.

Make MethodSecurityExpressionHandler utilisez 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;
    }
}

Ceci 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é depuis 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" />

Remarque: l'annotation @Secured n'acceptera pas ce remplacement car il s'exécute via un autre gestionnaire de validation. Donc, dans le XML ci-dessus, je les ai désactivés pour éviter toute confusion ultérieure.

14
Joseph Lust