Использование статических переменных в аннотациях Spring
Я использую авторизацию аннотации весны следующим образом:
@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'
есть ли способ получить доступ к статическим переменным с предварительного блокирования средств на аннотации?
4 ответов
попробуйте следующее, которое использует язык выражений Spring для оценки типа:
@PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");
обязательно укажите полное имя класса.
чтобы можно было писать выражения без имен пакетов:
<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;
}
}
теперь создать свою.образец.Роли.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')")
принятый ответ от Кевина Бауэрсокса работает, но мне не понравилось иметь T(полностью.квалифицированный.path), поэтому я продолжал искать. Я начал с создания пользовательского метода безопасности, используя ответ от Джеймса Уоткинса здесь:
как создать пользовательские методы для использования в аннотациях языка выражений spring security
однако вместо строки я использовал свои перечисления.Класс разрешений в качестве типа параметра:
@Component
public class MySecurityService {
public boolean hasPermission(enums.Permissions permission) {
...do some work here...
return true;
}
}
Теперь аккуратная часть что когда я вызываю hasPermission из аннотации, мне не нужно вводить весь путь, но я должен заключить его в одинарные кавычки:
@PreAuthorize("@mySecurityService.hasPermission('SOME_ROLE_NAME')")
поскольку метод hasPermission ожидает перечисление, он автоматически найдет значение перечисления с этим именем. Если он не найдет его, вы получите исключение:
org.springframework.expression.spel.SpelEvaluationException: Type conversion problem, cannot convert from java.lang.String to enums.Permissions
вы можете переименовать hasPermission в hasRole, в этом случае единственный компромисс заключается в том, что вы торгуете T(полностью.квалифицированный.путь) @mySecurityService и дополнительные одинарные кавычки.
не уверен, что это лучше, но это так. Поскольку ничто из этого не будет проверять значения во время компиляции, мой следующий шаг-сделать процессор аннотаций.
Я также должен отдать должное krosenvold за указание, что spring может автоматически преобразовываться в перечисление: https://stackoverflow.com/a/516899/618881
попробуйте что-то вроде этого:
@PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");
если ваше перечисление другого класса объявлено как public static, вам нужно использовать $ sign:
@PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");
name()
для предотвращения будущих проблем, если toString()
будет переопределено позднее