java - Spring 启动: Configure custom MethodSecurityExpressionOperations?

标签 java spring spring-boot spring-security configuration

我在解决我曾经工作过的 Spring Boot 安全配置时遇到问题,但现在无法识别我的自定义定义。我的目标是通过 Spring 中的方法级安全性和自定义注释来保护我们的所有服务。

当我启动服务时,我的 CustomMethodSecurityConfig 被实例化并调用 createExpressionHandler(),但是当我向服务发出请求时,它不会在我的 CustomMethodSecurityExpressionHandler 上调用 createSecurityExpressionRoot(...),而是在 DefaultWebSecurityExpressionHandler 上调用。

我很感激任何人能够提供关于为什么 Spring Security 无法识别我在 CustomMethodSecurityExpressionRoot 中定义的表达式的任何见解。

这是我的 GlobalMethodSecurityConfiguration 类的片段

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration {

  private final MyService1 myService1;
  private final MyService2 myService2;
  private final MyService3 myService3;

  @Autowired
  public CustomMethodSecurityConfig(MyService1 myService1, MyService2 myService2,
                                    MyService3 myService3) {
    this.myService1 = myService1;
    this.myService2 = myService2;
    this.myService3 = myService3;
  }

  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() {
    CustomMethodSecurityExpressionHandler expressionHandler =
        new CustomMethodSecurityExpressionHandler(myService1, myService2, myService3);
    expressionHandler.setPermissionEvaluator(permissionEvaluator());
    return expressionHandler;
  }
}

这是我的 DefaultMethodSecurityExpressionHandler 类的片段

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

  private final MyService1 myService1;
  private final MyService2 myService2;
  private final MyService3 myService3;
  private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

  public CustomMethodSecurityExpressionHandler(MyService1 myService1, MyService2 myService2,
                                               MyService3 myService3) {
    this.myService1 = myService1;
    this.myService2 = myService2;
    this.myService3 = myService3;
  }

  @Override
  protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
                                                                            MethodInvocation invocation) {
    CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication,
                                                                                     myService1,
                                                                                     myService2,
                                                                                     myService3);

    root.setPermissionEvaluator(getPermissionEvaluator());
    root.setTrustResolver(this.trustResolver);
    root.setRoleHierarchy(getRoleHierarchy());

    return root;
  }
}

这是我的 SecurityExpressionRoot 的片段,这是我定义 SpEL 表达式的地方,我在服务的注释中使用它。我只提供了一个简化的 isUser 作为示例。这些方法的作用并不重要,重要的是它们是可见的。

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot
    implements MethodSecurityExpressionOperations {

  private Object filterObject;
  private Object returnObject;

  private MyService1 myService1;
  private MyService2 myService2;
  private MyService3 myService3;

  public CustomMethodSecurityExpressionRoot(
      Authentication authentication,
      MyService1 myService1,
      MyService2 myService2,
      MyService3 myService3) {
    super(authentication);
    this.myService1 = myService1;
    this.myService2 = myService2;
    this.myService3 = myService3;
  }

  @Override
  public Object getFilterObject() {
    return this.filterObject;
  }

  @Override
  public Object getReturnObject() {
    return this.returnObject;
  }

  @Override
  public void setFilterObject(Object obj) {
    this.filterObject = obj;
  }

  @Override
  public void setReturnObject(Object obj) {
    this.returnObject = obj;
  }

  @Override
  public Object getThis() {
    return this;
  }

  //All custom SpEL methods
  public boolean isUser(Long userId) {
    SecurityUser user = (SecurityUser) this.getPrincipal();
    return user.getUserId() == userId;
  }

  ...

}

最后,这是我的 WebSecurityConfigurerAdapter 的一个片段,它串联使用,它验证来 self 们的 UAA 服务器的外部身份验证 token 。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
    prePostEnabled = true,
    proxyTargetClass = true)
public class ServiceSecurityConfig extends WebSecurityConfigurerAdapter {

  private final TokenCheckService _tokenCheckService;

  @Autowired
  ServiceSecurityConfig(TokenCheckService tokenCheckService) {
    _tokenCheckService = tokenCheckService;
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(new TokenAuthenticationProvider(_tokenCheckService));
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers(HttpMethod.OPTIONS, "/api/**");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
        http
            .anonymous()
              .disable()
            .csrf()
              .disable()
            .exceptionHandling()
              .authenticationEntryPoint(new UnAuthorizedEntryPoint())
              .and()
            .sessionManagement()
              .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
              .and()
            .authorizeRequests()
              .anyRequest().authenticated();
    http.addFilterBefore(new AuthenticationTokenFilter(), BasicAuthenticationFilter.class);
  }
}

编辑: 我似乎认为这是我的 WebDecisionVoters 在初始化期间被覆盖的问题。如果我在肯定构造函数中有一个断点

AffirmativeBased(List<AccessDecisionVoter<? extends Object>> decisionVoters)

我可以看到 AffirmativeBased 被实例化为 3 个决策投票者,其中之一是 PreInitationAuthorizationAdviceVoter,其中包含对我的表达式处理程序的引用。我相信这是由 methodSecurityInterceptor 的 bean 实例化创建的。

当我继续断点时,我再次命中相同的基于 Affirmative 的构造函数,但只有一个决策投票者,即引用 DefaultWebSecurityExpressionHandler 实例的 WebExperssionVoter。我相信这是由 springSecurityFilterChain 的 bean 实例化创建的。

最佳答案

我按照Custom SecurityExpression with Service中的步骤解决了这个问题。 。问题似乎出在我的自动连线服务上,这些服务与安全性是分开的。 MyService1、MyService2 和 MyService3 导致了问题,将它们删除可以让安全性发挥作用。

任何附加服务都必须在扩展 DefaultMethodSecurityExpressionHandler 的类的 createSecurityExpressionRoot 中设置。

@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
    CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
    // Other initialization
    root.setMyService1(applicationContext.getBean(MyService1.class));
    root.setMyService2(applicationContext.getBean(MyService2.class));
    root.setMyService3(applicationContext.getBean(MyService3.class));
    return root;
}

关于java - Spring 启动: Configure custom MethodSecurityExpressionOperations?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58224516/

相关文章:

java - 使用 <mvc :annotation-driven/> application is not deploy by tomcat in netbeans 时出错

java - 使用默认值声明 Thymeleaf 变量后如何对其进行算术运算?

java - 如何将 Excel 中的图表导出为图形

java - 如何避免多线程程序中不必要的阻塞?

Java 将元素附加到 XML 文档

java - 复选框和相应行的值从 jsp 到 java Controller 文件

java - 如何从 java 中的字符串中删除这些子字符串?

database - Spring Framework 中的默认隔离级别

spring - 刷新 Spring Boot 自动配置的属性

java - 如何从 Spring Boot 中的 Rest Controller 中的 REST 请求获取日历字段?