Ответ 1
Попробуйте следующее, которое использует выражение Spring Expression Language для определения типа:
@PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");
Обязательно укажите полное имя класса.
Я использую аннотацию 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?
Попробуйте следующее, которое использует выражение Spring Expression Language для определения типа:
@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;
}
}
Теперь создайте файл 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')")
Принятый ответ от Кевина Боуэрсокса работает, но мне не нравилось иметь материал T (full.qualified.path), поэтому я продолжал смотреть. Я начал с создания пользовательского метода безопасности, используя ответ Джеймса Уоткинса здесь:
Однако вместо 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/...
Попробуйте что-то вроде этого:
@PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");
Если ваше перечисление OtherClass объявлено как public static, вам нужно использовать знак $:
@PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");
name()
для предотвращения проблем с фьютером, если toString()
будет переопределен позже