2010-05-17 15 views
27

J'ai une classe de service implémentée dans Java 6/Spring 3 qui nécessite une annotation pour restreindre l'accès par rôle.Point de coupure AOP de ressort qui correspond à l'annotation sur l'interface

J'ai défini une annotation appelée RequiredPermission qui a comme valeur de l'attribut une ou plusieurs valeurs d'un ENUM appelé OperationType:

public @interface RequiredPermission { 

/** 
* One or more {@link OperationType}s that map to the permissions required 
* to execute this method. 
* 
* @return 
*/ 
OperationType[] value();} 

public enum OperationType { 
     TYPE1, 
     TYPE2; 
} 

package com.mycompany.myservice; 
public interface MyService{ 
    @RequiredPermission(OperationType.TYPE1) 
    void myMethod(MyParameterObject obj); 
} 

package com.mycompany.myserviceimpl; 
public class MyServiceImpl implements MyService{ 
    public myMethod(MyParameterObject obj){ 
     // do stuff here 
    } 
} 

j'ai aussi la définition d'aspect suivant:

/** 
* Security advice around methods that are annotated with 
* {@link RequiredPermission}. 
* 
* @param pjp 
* @param param 
* @param requiredPermission 
* @return 
* @throws Throwable 
*/ 
@Around(value = "execution(public *" 
     + " com.mycompany.myserviceimpl.*(..))" 
     + " && args(param)" + // parameter object 
     " && @annotation(requiredPermission)" // permission annotation 

, argNames = "param,requiredPermission") 
public Object processRequest(final ProceedingJoinPoint pjp, 
     final MyParameterObject param, 
     final RequiredPermission requiredPermission) throws Throwable { 
    if(userService.userHasRoles(param.getUsername(),requiredPermission.values()){ 
     return pjp.proceed(); 
    }else{ 
     throw new SorryButYouAreNotAllowedToDoThatException(
      param.getUsername(),requiredPermission.value()); 
    } 
} 

Le L'objet paramètre contient un nom d'utilisateur et je souhaite rechercher le rôle requis pour l'utilisateur avant de permettre l'accès à la méthode. Quand je mets l'annotation sur la méthode dans MyServiceImpl, tout fonctionne très bien, le pointcut est assorti et l'aspect intervient. Cependant, je crois que l'annotation fait partie du contrat de service et devrait être publiée avec l'interface dans un package API distinct. Et évidemment, je ne voudrais pas mettre l'annotation à la fois sur la définition de service et sur l'implémentation (DRY).

Je sais qu'il y a des cas dans Spring AOP où les aspects sont déclenchés par des annotations à une méthode d'interface (par exemple transactionnelle). Y a-t-il une syntaxe spéciale ici ou est-ce simplement impossible hors de la boîte. PS: Je n'ai pas posté ma config de printemps, car il semble fonctionner très bien. Et non, ce ne sont ni mes noms de classe ni de méthode d'origine.

PPS: En fait, voici la partie pertinente de ma config de printemps:

<aop:aspectj-autoproxy proxy-target-class="false" /> 

<bean class="com.mycompany.aspect.MyAspect"> 
    <property name="userService" ref="userService" /> 
</bean> 
+0

Donc, si je comprends bien, la meilleure solution que vous avez trouvé pour votre question initiale (c.-à-ayant l'annotation dans le interface, pas dans la mise en œuvre) est de faire correspondre l'ensemble du paquet d'interfaces du fournisseur de services, puis introspecter sur le ProceedingJoinPoint afin de trouver les annotations de la méthode dans l'interface? Est-ce que c'est possible? C'est un très bon sujet, et je ne veux pas en commencer un nouveau avec la même question! Merci!! – espinchi

+0

@espinchi J'étais tellement frustré que j'ai copié toutes les annotations de l'interface vers la classe d'implémentation et appliqué cela avec un test unitaire rigoureux qui vérifiait toutes les méthodes sur l'interface et la classe d'implémentation pour des annotations identiques. Pas exactement AOP, je sais. Je dirais que ça ne marche pas de manière fiable, après tout. –

Répondre

28

Si je vous comprends corriger, vous voulez un pointcut qui trouve toutes les méthodes dans les classes qui étend MyService et est annotés et avec le arguments préférés.

Je vous propose de remplacer:

execution(public * com.mycompany.myserviceimpl.*(..)) 

avec:

execution(public * com.mycompany.myservice.MyService+.*(..)) 

Le signe plus est utilisé si vous voulez un point de jonction pour correspondre à la classe MyService ou une classe qui la prolonge.

J'espère que ça aide!

+0

Il y a une petite faute de frappe. c'est en fait: exécution (public * com.mycompany.myservice.MyService +. * (..)) Mais cela fonctionne comme un charme. Merci beaucoup!!! –

+0

C'est gentil! Je vais corriger ma réponse pour inclure un joker sur toutes les méthodes. – Espen

+1

J'ai réalisé beaucoup plus tard que ce n'est pas tout à fait correct (bien que ce soit probablement le meilleur pari). Je voulais que l'annotation soit sur la méthode d'interface de service, mais cela ne fonctionne que si l'annotation est sur la méthode d'implémentation. Et puisque j'ai aussi besoin de ce comportement: http://stackoverflow.com/questions/3100228/spring-aop-pointcut-expression-to-access-method-return-type, je dois garder le pointcut minimal et vérifier l'objet proceedjoinpoint moi même. –

4

Espen, votre code ne fonctionne que pour une seule classe.

execution(public * com.mycompany.myservice.MyService+.*(..)) 

mais si je veux que ce comportement pour tous les services * com.mycompany.services package **?

+1

cela fonctionne très bien: exécution (public * com.mycompany.myservice. * +. * (..)) –

0

J'ai rencontré le même problème, mais les suggestions ci-dessus ne m'aident pas. Si j'ai seulement cela comme pointcut:

execution(public * com.mycompany.myservice.MyService+.*(..)) 

Ensuite, les conseils est invoqué. Toutefois, si j'ajoute ceci:

&& @annotation(x.y.z.Logged) 

Ensuite, le conseil n'est pas invoqué. L'interface MyService contient une annotation @Logged sur l'une de ses méthodes et même sur sa déclaration de type.

+1

essayez d'utiliser && @annotation (myparam) et laissez-vous aspect avoir un paramètre nommé myparam de type Logged (même si vous n'en ai pas besoin) –

+1

Cela ne marche pas non plus. Voici mon conseil: ". (Exécution abcdservices publique * * * + (..)) &&" @Around (valeur = \t \t \t + "cible (haricot) &&" \t \t \t + « @annotation (connecté) ", argNames =" bean, connecté ") – user348237

+1

yup, c'est vrai. L'annotation doit être sur la méthode d'implémentation, pas sur la méthode de l'interface de service. –

0

Vous devez ajouter l'annotation @Inherited à votre annotation @RequiredPermission.

http://download.oracle.com/javase/6/docs/api/java/lang/annotation/Inherited.html

+6

'@ Inherited ne fonctionne que sur le niveau de la classe, pas sur le niveau de la méthode:" Notez que ce type de méta-annotation n'a aucun effet si le type annoté est utilisé pour annoter autre chose qu'une classe. provoque des annotations à hériter des superclasses, les annotations sur les interfaces implémentées n'ont aucun effet. " –

+0

Je ne le savais pas ... merci! –