#java #spring #spring-boot #spring-security
Вопрос:
Я знаю, что у Spring Security есть абстрактный класс SecurityExpressionRoot
. В этом у нас реализованы такие методы , как hasAuthority(String var1)
и hasRole(String var1)
т. Д. Spring также предоставляет @PreAuthorize
аннотацию, которая будет использоваться на уровне метода, мы передаем одно значение в этой аннотации, например
@PreAuthorize("hasRole('ROLE_ABC')")
Аннотация @interface
выглядит так
package org.springframework.security.access.prepost;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
String value();
}
Я хочу знать, как эта аннотация запускает конкретный метод SecurityExpressionRoot
.
Ответ №1:
Spring security использует аспектно-ориентированное программирование (AOP) для вплетения/переплетения кода безопасности в вашу собственную базу кода. Весной это работает с помощью аннотаций, определяющих точки ввода (cfr. pointcuts
), чтобы разрешить выполнение дополнительной логики до/после/в вашем собственном коде (cfr. advice
).
Interceptors
отсканируйте свою кодовую базу на join points
предмет (т. Е. для Spring это всегда выполнение метода, отмеченное конкретными аннотациями) и выполнит дополнительную конкретную логику в зависимости от того, какую точку перехвата (т. е. интерфейс) вы использовали.
Чтобы включить такое поведение, можно добавить конфигурацию:
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class ConfigGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
...
}
В частности, для предварительной авторизации PrePostAdviceReactiveMethodInterceptor отвечает за поиск ваших методов с аннотациями PreAutorize
. В свою очередь, это приведет к делегированию, для PreInvocationAuthorizationAdvice
которого настроена здесь конфигурация ReactiveMethodSecurityConfiguration как ExpressionBasedPreInvocationAdvice.
Внутренне при этом используется обработчик выражений по умолчанию Defaultmethod Securityexpressionhandler, который создает SecurityExpressionRoot
. Фактическая реализация этого SecurityExpressionRoot
определит, как будет обрабатываться ваше выражение в вашей PreAuthorize
воле и какая логика должна быть выполнена.
Корень SecurityExpressionRoot определяет , какие выражения разрешены в вашем PreAuthorize
, например hasRole
.
Чтобы добавить дополнительные выражения или расширить логику разрешений по умолчанию, вам необходимо предоставить пользовательскую реализацию SecurityExpressionRoot
с дополнительным пользовательским PermissionEvaluator
. Например, если вы хотите написать @PreAuthorize("hasKnowledgeOf('AOP')")
.
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot
implements MethodSecurityExpressionOperations {
private final PermissionEvaluator permissionEvaluator;
private final Authentication authentication;
private Object filterObject;
private Object returnObject;
private Object target;
public CustomMethodSecurityExpressionRoot(
Authentication authentication,
PermissionEvaluator permissionEvaluator) {
super(authentication);
this.authentication = authentication;
this.permissionEvaluator = permissionEvaluator;
super.setPermissionEvaluator(permissionEvaluator);
}
// new expression to check if the requested knowledge is present
public boolean hasKnowledgeOf(String context) {
// provide logic that performs the check
}
@Override
public void setFilterObject(Object filterObject) {
this.filterObject = filterObject;
}
@Override
public Object getFilterObject() {
return filterObject;
}
@Override
public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}
@Override
public Object getReturnObject() {
return returnObject;
}
@Override
public Object getThis() {
return target;
}
}
и
@Configuration
public class CustomPermissionEvaluator
implements PermissionEvaluator {
@Override
public boolean hasPermission(
Authentication authentication,
Object targetDomainObject,
Object permission) {
// define your custom permission logic here
}
@Override
public boolean hasPermission(
Authentication authentication,
Serializable targetId,
String targetType,
Object permission) {
// define your custom permission logic here
}
}
Чтобы завершить настройку и передать средство оценки в корень выражения.
public class CustomMethodSecurityExpressionHandler
extends DefaultMethodSecurityExpressionHandler {
PermissionEvaluator permissionEvaluator;
public CustomMethodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
super.setPermissionEvaluator(permissionEvaluator);
}
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication,
MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(
authentication,
permissionEvaluator);
root.setTrustResolver(new AuthenticationTrustResolverImpl());
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
и
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class ConfigGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
@Autowired CustomPermissionEvaluator permissionEvaluator;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new CustomMethodSecurityExpressionHandler(permissionEvaluator);
}
}
Комментарии:
1. Очень признателен, приятель.
Ответ №2:
Аннотация ничего не делает и является не чем иным, как метаданными. Добавление аннотации к методу и без фрагмента кода, который действует на эту аннотацию, ничего не делает. Итак, вам нужен какой-то обработчик аннотаций.
Для большинства аннотаций, используемых в Spring, это означает, что они применяются AOP (Аспектно-ориентированное программирование). Весной это означает, что по умолчанию это делается на основе прокси-сервера. Этот прокси перехватывает фактический вызов метода, применяет некоторую дополнительную логику до/после вызова фактического метода.
Дополнительная логика обеспечивается a MethodInterceptor
или @Aspect
при использовании аспектов, основанных на AspectJ. В случае @PreAuthorize
аннотации это делается с помощью MethodSecurityInterceptor
.
Что MethodSecurityInterceptor
делает, на основе @PreAuthorize
аннотации и выражения, написанного там (выражение является выражением SpEL), определяет, имеет ли текущий пользователь право доступа к методу.
Это MethodSescurityInterceptor
может быть включено либо вручную (выполнение всей конфигурации самостоятельно, довольно утомительно и подвержено ошибкам), либо путем добавления @EnableGlobalMethodSecurifty
аннотации. Последний добавит @Configuration
класс и некоторый процессор для автоматической настройки необходимых классов инфраструктуры.
Ответ №3:
Вызов аспекта после оценки строки, указанной в аннотации @PreAuthorize.
Для получения подробной информации, я думаю, вам нужно покопаться в исходном коде Spring. Может быть, начнем отсюда: https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java#nn
Ответ №4:
@Предварительная авторизация равна @Защищенным и @разрешенным ролям.
@PreAuthorize("hasRole('ROLE_SPITTER')")
public void addSpittle(Spittle spittle) {
// ...
}
Но строковый аргумент для @PreAuthorize-это выражение SpEL
С помощью выражений SpEL
, определяющих решения о доступе, можно записать гораздо более сложные ограничения безопасности
@PreAuthorize(
"(hasRole('ROLE_SPITTER') and #spittle.text.length() <= 140)"
"or hasRole('ROLE_PREMIUM')")
public void addSpittle(Spittle spittle) {
// ...
}