2010-05-17 18 views
27

Tengo una clase de servicio implementada en Java 6/Spring 3 que necesita una anotación para restringir el acceso por rol.Punto de corte AOP Spring que coincide con la anotación en la interfaz

he definido un llamado RequiredPermission anotación que tiene como su valor de atributo uno o más valores de una enumeración llamado 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 
    } 
} 

I también tienen la siguiente definición aspecto:

/** 
* 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()); 
    } 
} 

El El objeto parámetro contiene un nombre de usuario y quiero buscar el rol requerido para el usuario antes de permitir el acceso al método.

Cuando pongo la anotación en el método en MyServiceImpl, todo funciona bien, el pointcut es coincidente y el aspecto entra en acción. Sin embargo, creo que la anotación es parte del contrato de servicio y debe publicarse con la interfaz en un paquete API por separado Y obviamente, no me gustaría poner la anotación tanto en la definición del servicio como en la implementación (DRY).

Sé que hay casos en Spring AOP donde los aspectos se activan mediante las anotaciones de un método de interfaz (por ejemplo, Transactional). ¿Hay una sintaxis especial aquí o es simplemente imposible de usar?

PD: No he publicado mi configuración de muelles, ya que parece estar funcionando bien. Y no, esos no son mis nombres originales de clase o método.

PPS: En realidad, esta es la parte pertinente de mi config primavera:

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

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

lo tanto, si he entendido bien, la mejor solución que encontró para su pregunta original (es decir, con la anotación en el interfaz, no en la implementación) es hacer coincidir todo el paquete de interfaces del proveedor de servicios, y luego introspectivamente en el ProceedingJoinPoint con el fin de encontrar las anotaciones del método en la interfaz. ¿Es eso posible? ¡Este es un gran hilo, y no quiero comenzar uno nuevo con la misma pregunta! ¡¡Gracias!! – espinchi

+0

@espinchi en realidad estaba tan frustrado que copié todas las anotaciones de la interfaz a la clase de implementación y lo hice cumplir con una rigurosa prueba de unidad que verificaba todos los métodos tanto en la interfaz como en la clase de implementación para anotaciones idénticas. No exactamente AOP, lo sé. Diría que no funciona de manera confiable, después de todo –

Respuesta

28

Si entiendo a corregir, usted quiere un punto de corte que encuentra todos los métodos en las clases que se extiende MyService y están anotadas, y con la argumentos preferidos.

propongo que reemplace:

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

con:

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

El signo más se utiliza si quieres un joinpoint para que coincida con la clase MyService o una clase que amplía.

Espero que ayude!

+0

Hay un pequeño error tipográfico. en realidad es: ejecución (public * com.mycompany.myservice.MyService +. * (..)) Pero funciona a las mil maravillas. ¡¡¡Muchas gracias!!! –

+0

¡Eso es bueno! Corregiré mi respuesta para incluir un comodín en todos los métodos también. – Espen

+1

Me di cuenta mucho más tarde de que esto no es del todo correcto (aunque esta es probablemente la mejor opción). Quería que la anotación estuviera en el método de interfaz de servicio, pero esto solo funciona si la anotación está en el método de implementación. Y como también necesito este comportamiento: http://stackoverflow.com/questions/3100228/spring-aop-pointcut-expression-to-access-method-return-type, tengo que mantener el punto de corte mínimo y verificar el siguiente punto de encuentro del objeto mí mismo. –

4

Espen, el código funciona sólo para una clase:.

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

pero lo que si quiero este comportamiento para todos los servicios en com.mycompany.services * ** paquete?

+1

esto funciona bien: ejecución (public * com.mycompany.myservice. * +. * (..)) –

0

Me he encontrado con el mismo problema, sin embargo, las sugerencias anteriores no ayudan. Si sólo tengo esto como un punto de corte:

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

A continuación, se invoca el consejo. Sin embargo, si agrego esto:

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

Entonces el consejo no se invoca. La interfaz MyService contiene una anotación @Logged en uno de sus métodos e incluso en su declaración de tipo.

+1

intente usar && @annotation (myparam) y le permite tener un parámetro llamado myparam del tipo Logged (incluso si no lo necesita) –

+1

Esto tampoco funciona. Aquí está mi consejo: ". De ejecución (abcdservices públicas * * + * (..)) &&" @Around (valor = \t \t \t + "objetivo (frijol) &&" \t \t \t + "@annotation (logged) ", argNames =" bean, logged ") – user348237

+1

sí, eso es correcto. La anotación debe estar en el método de implementación, no en el método de interfaz de servicio. –

0

Debe agregar la anotación @Hermitida a su anotación @RequiredPermission.

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

+6

'@ Inherited' solo funciona en el nivel de clase, no en el nivel de método:" Tenga en cuenta que este tipo de meta-anotación no tiene efecto si el tipo anotado se usa para anotar cualquier cosa que no sea una clase. Tenga en cuenta también que esta meta-anotación hace que las anotaciones se hereden de las superclases; las anotaciones en las interfaces implementadas no tienen ningún efecto ". –

+0

No sabía eso ... ¡gracias! –

Cuestiones relacionadas