Spring AOP pointcut, который соответствует аннотации на интерфейсе - PullRequest
28 голосов
/ 17 мая 2010

У меня есть класс обслуживания, реализованный в Java 6 / Spring 3, который нуждается в аннотации для ограничения доступа по роли.

Я определил аннотацию под названием RequiredPermission, которая имеет в качестве атрибута значения одно или несколько значений из перечисления с именем 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
   }
}

У меня также есть следующее определение аспекта:

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

Объект параметра содержит имя пользователя, и я хочу найти необходимую роль для пользователя, прежде чем разрешить доступ к методу.

Когда я помещаю аннотацию для метода в MyServiceImpl, все работает просто отлично, точка совпадает, и аспект включается. Однако я считаю, что аннотация является частью контракта на обслуживание и должна быть опубликована с интерфейсом отдельный пакет API. И, очевидно, я не хотел бы помещать аннотацию как для определения службы, так и для ее реализации (DRY).

Я знаю, что в Spring AOP есть случаи, когда аспекты запускаются аннотациями одного из методов интерфейса (например, Транзакционный). Здесь есть специальный синтаксис или он просто невозможен из коробки.

PS: я не опубликовал свой весенний конфиг, так как он, кажется, работает просто отлично. И нет, это не мои оригинальные имена классов и методов.

PPS: Собственно, вот соответствующая часть моей весенней конфигурации:

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

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

Ответы [ 5 ]

31 голосов
/ 17 мая 2010

Если я правильно вас понял, вам нужен pointcut, который находит все методы в классах, который расширяет MyService и имеет аннотации с предпочтительными аргументами.

Предлагаю заменить:

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

с:

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

Знак плюс используется, если вы хотите, чтобы точка соединения соответствовала классу MyService или классу, который его расширяет.

Надеюсь, это поможет!

4 голосов
/ 18 мая 2010

Espen, ваш код работает только для одного класса:

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

но что, если я хочу, чтобы это поведение было для всех служб в пакете * com.mycompany.services. **?

1 голос
/ 28 мая 2012

У меня был успех с этим подходом:

http://nonrepeatable.blogspot.co.uk/2010/05/validating-service-method-parameters.html

0 голосов
/ 11 июля 2011

Вы должны добавить аннотацию @Inherited к аннотации @RequiredPermission.

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

0 голосов
/ 23 мая 2010

Я столкнулся с той же проблемой, но приведенные выше предложения не помогают. Если у меня есть только это как pointcut:

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

Тогда вызывается совет. Однако, если я добавлю это:

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

Тогда совет не вызывается. Интерфейс MyService содержит аннотацию @Logged для одного из своих методов и даже для объявления типа.

...