Comment créer des méthodes personnalisées à utiliser dans les annotations de langage des expressions de sécurité Spring

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

Par exemple, je voudrais créer une méthode personnalisée comme «customMethodReturningBoolean» pour être utilisée 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 pourrais-je procéder pour le configurer dans les fichiers de configuration xml du spring et que quelqu’un me donne un exemple de méthode personnalisée utilisée de cette manière?

Vous devrez sous-classer deux classes.

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

    

myMethodSecurityExpressionHandler sera une sous-classe de DefaultMethodSecurityExpressionHandler qui remplace createEvaluationContext() , en définissant une sous-classe de MethodSecurityExpressionRoot sur 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; } 

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

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

  ...  

Créez un haricot comme celui-ci:

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

Alors faites quelque chose comme ça dans votre jsp:

    

Ou annoter une méthode:

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

Et souvenez-vous: si vous utilisez Spring et que vous devez résoudre un problème en étendant des classes, en substituant des méthodes, en implémentant des interfaces, etc., vous faites probablement quelque chose de mal. C’est toutes les annotations et xml, c’est pourquoi nous aimons tellement le spring et pas (les anciennes versions de) EJB.

De plus, vous pouvez utiliser Spring Expression Language dans vos annotations @PreAuthorize pour accéder à l’authentification actuelle ainsi qu’aux arguments de méthode.

Par exemple:

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

Ensuite, mettez à jour votre @PreAuthorize pour qu’il corresponde à la nouvelle signature de la méthode:

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

Merci ericacm , mais ça ne marche pas pour quelques raisons:

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

Les différences sont que nous appelons la méthode createEvaluationContext existante, puis ajoutons notre object racine personnalisé. Enfin, je viens de retourner un type d’ object 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.

Faites en sorte que MethodSecurityExpressionHandler utilise 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 EvaluationContext 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(Ssortingng expression) { return hasRole(expression); } } 

Enfin, mettez à jour securityContext.xml (et assurez-vous qu’il est référencé depuis votre applcationContext.xml):

       

Remarque: l’annotation @Secured n’acceptera pas cette substitution car elle passe par un gestionnaire de validation différent. Ainsi, dans le xml ci-dessus, je les ai désactivés pour éviter toute confusion ultérieure.