Использование статических переменных в аннотациях 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?


person RobEarl    schedule 03.07.2013    source источник


Ответы (5)


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

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

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

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

person Kevin Bowersox    schedule 03.07.2013
comment
Работает, но это все еще интерпретируемая строка, поэтому Eclipse не увидит ее при рефакторинге, например. имя я думаю. - person yglodt; 26.07.2016

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

<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)")

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

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

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

    @PreAuthorize("hasRole('ROLE_AUTHENTICATEDDDD')")
person Magnus Heino    schedule 17.06.2014
comment
Хорошее решение. Было бы неплохо иметь ошибки запуска, но я не думаю, что вы можете добиться этого таким образом. Оценка по-прежнему происходит во время выполнения. Лучшего решения пока не нашел... - person chris; 10.10.2017
comment
Я думаю, что ответ Кевина Бауэрсокса - хороший ответ, но ответ @MagnusHeino - более полное решение, потому что класс, включающий полный пакет, выглядит немного некрасиво, тем не менее, оба ответа верны. - person hermeslm; 11.12.2018

Вы также можете создать bean-контейнер с ролями, например:

@Component("R")
public final class RoleContainer {
  public static final String ROLE_A = "ROLE_A";
}

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

@PreAuthorize("hasRole(@R.ROLE_A)")
person Алексей Виноградов    schedule 07.04.2020
comment
Самый короткий и аккуратный ответ! - person Alexander Melnichuk; 25.11.2020

Принятый ответ от Кевина Бауэрсокса работает, но мне не нравился материал 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 может автоматически преобразовываться в перечисление: https://stackoverflow.com/a/516899/618881

person Henry    schedule 17.07.2013

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

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

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

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

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

person Maksym Demidas    schedule 03.07.2013