谁能深入了解 @PreAuthorize 注释的工作原理?

Jit*_*M V 11 java spring spring-security spring-boot

我知道 Spring Security 有一个抽象类SecurityExpressionRoot。我们已经实现了诸如 等hasAuthority(String var1)方法hasRole(String var1)。Spring 还提供了一个@PreAuthorize在方法级别使用的注释,我们在该注释中传递单个值,例如

@PreAuthorize("hasRole('ROLE_ABC')")
Run Code Online (Sandbox Code Playgroud)

注释@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();
}
Run Code Online (Sandbox Code Playgroud)

我想知道这个注释如何触发SecurityExpressionRoot.

chv*_*ndb 21

Spring security 使用面向方面编程 ( AOP ) 将安全代码编织/交织到您自己的代码库中。Spring 中的工作方式是使用定义注入点的注释 (cfr. pointcuts) 来允许在您自己的代码之前/之后/内执行额外的逻辑 (cfr. advice)。

Interceptors扫描您的代码库join points(即,对于 Spring,当标记有特定注释时,这始终是方法执行)并将根据您使用的拦截点(即接口)执行其他特定逻辑。

要启用此行为,可以添加一项配置:

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class ConfigGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
  ...
}
Run Code Online (Sandbox Code Playgroud)

特别是对于PreAuthorize , PrePostAdviceReactiveMethodInterceptor负责查找用 注释的方法PreAutorize。反过来,这将委托给PreInvocationAuthorizationAdvice此处配置的ReactiveMethodSecurityConfiguration作为ExpressionBasedPreInitationAdvice

在内部,这使用默认表达式处理程序DefaultMethodSecurityExpressionHandler创建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;
  }
}
Run Code Online (Sandbox Code Playgroud)

@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
  }
}
Run Code Online (Sandbox Code Playgroud)

完成配置并将评估器传递给表达式根。

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class ConfigGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {

  @Autowired CustomPermissionEvaluator permissionEvaluator;

  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() {
    return new CustomMethodSecurityExpressionHandler(permissionEvaluator);
  }
}
Run Code Online (Sandbox Code Playgroud)