Использование статических переменных в аннотациях Spring

Я использую аннотацию spring PreAuthorize следующим образом:

@PreAuthorize("hasRole('role')");

Однако у меня уже есть "роль", определяемая как статическая строка в другом классе. Если я попытаюсь использовать это значение:

@PreAuthorize("hasRole(OtherClass.ROLE)");

Я получаю сообщение об ошибке:

org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 14): Field or property 'OtherClass' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot'

Есть ли способ доступа к статическим переменным, подобным этому, с аннотацией PreAuthorize?

Ответ 1

Попробуйте следующее, которое использует выражение Spring Expression Language для определения типа:

@PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");

Обязательно укажите полное имя класса.

Документация

Ответ 2

Чтобы можно было писать выражения без имен пакетов:

<sec:global-method-security>
    <sec:expression-handler ref="methodSecurityExpressionHandler"/>
</sec:global-method-security>

<bean id="methodSecurityExpressionHandler" class="my.example.DefaultMethodSecurityExpressionHandler"/>

Затем расширьте DefaultMethodSecurityExpressionHandler:

public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {

    @Override
    public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
        StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
        ((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport("my.example");
        return standardEvaluationContext;
    }
}

Теперь создайте файл my.example.Roles.java:

public class Roles {

    public static final String ROLE_UNAUTHENTICATED = "ROLE_UNAUTHENTICATED";

    public static final String ROLE_AUTHENTICATED = "ROLE_AUTHENTICATED";
}

И ссылайтесь на него без имени пакета в аннотации:

@PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATED)")

вместо:

@PreAuthorize("hasRole(T(my.example.Roles).ROLE_AUTHENTICATED)")

Делает его более читаемым imho. Также теперь набираются роли. Запись:

@PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATEDDDD)")

и вы получите ошибки запуска, которые не были бы там, если бы вы написали:

    @PreAuthorize("hasRole('ROLE_AUTHENTICATEDDDD')")

Ответ 3

Принятый ответ от Кевина Боуэрсокса работает, но мне не нравилось иметь материал T (full.qualified.path), поэтому я продолжал смотреть. Я начал с создания пользовательского метода безопасности, используя ответ Джеймса Уоткинса здесь:

Как создать пользовательские методы для использования в spring аннотациях выражения выражения безопасности

Однако вместо String я использовал свой класс enums.Permissions как тип параметра:

@Component
public class MySecurityService {
    public boolean hasPermission(enums.Permissions permission) {

        ...do some work here...

        return true;
    }
}

Теперь аккуратная часть заключается в том, что когда я вызываю hasPermission из аннотации, мне не нужно вводить весь путь, но мне нужно заключить его в одинарные кавычки:

@PreAuthorize("@mySecurityService.hasPermission('SOME_ROLE_NAME')")

Поскольку метод hasPermission ожидает Enum, он автоматически найдет значение Enum с этим именем. Если он не находит его, вы получите исключение:

org.springframework.expression.spel.SpelEvaluationException: Type conversion problem, cannot convert from java.lang.String to enums.Permissions

Вы можете переименовать hasPermission в hasRole, и в этом случае единственным компромиссом является то, что вы торгуете T (full.qualified.path) для @mySecurityService и дополнительных одинарных кавычек.

Не уверен, что это лучше, но вот оно. Поскольку ничто из этого не собирается проверять значения во время компиляции, мой следующий шаг - сделать обработчик аннотации.

Я также должен отдать должное krosenvold за указание, что spring может автоматически конвертировать в перечисление: fooobar.com/questions/57970/...

Ответ 4

Попробуйте что-то вроде этого:

@PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");

Если ваше перечисление OtherClass объявлено как public static, вам нужно использовать знак $:

@PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");

name() для предотвращения проблем с фьютером, если toString() будет переопределен позже