使用自定义 bean 测试 PreAuthorize 注释

Posted

技术标签:

【中文标题】使用自定义 bean 测试 PreAuthorize 注释【英文标题】:Testing PreAuthorize annotations with custom beans 【发布时间】:2020-10-08 09:06:39 【问题描述】:

我正在尝试在 Spring Boot 中的服务方法上测试我的 PreAuthorize 注释。我已经设法让它评估 SPeL 表达式,但是在引用自定义 bean 时它会失败。

我正在测试的服务方法:

@PreAuthorize("hasPermission(#eventId.id(), @eventTypePermission.target, @eventTypePermission.write())")
  public EventTypeId retrieveEventTypeIdForEventId(EventId eventId) 
    return null;
  

测试代码:

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
class TestConfig extends GlobalMethodSecurityConfiguration 

  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() 
    DefaultMethodSecurityExpressionHandler expressionHandler =
      new DefaultMethodSecurityExpressionHandler();
    expressionHandler.setPermissionEvaluator(new PermissionEvaluatorProxyTest());
    return expressionHandler;
  

  @Bean
  public EventTypePermission permission() 
    return new EventTypePermission();
  


@Component
@Slf4j
class PermissionEvaluatorProxyTest implements PermissionEvaluator 
  @Autowired
  public PermissionEvaluatorProxyTest() 
  

  @Override
  public boolean hasPermission(
    Authentication authentication, Object targetType, Object permission) 
    return true;
  

  @Override
  public boolean hasPermission(
    Authentication authentication, Serializable targetId, String targetType, Object permission) 
    return true;
  


@Import(TestConfig.class)
@RunWith(SpringRunner.class)
@SpringBootTest(classes=EventUpdater.class)
public class EventUpdaterTest 
  @Autowired
  private EventUpdater eventUpdater;
  @MockBean
  private EventRepository repository;
  @MockBean
  private EventTypePermission eventTypePermission;

  @Before
  public void setup() 
  

  @Test
  @WithMockUser(username="test")
  public void test() 
    eventUpdater.retrieveEventTypeIdForEventId(new EventId(UUID.randomUUID()));
  

错误是:

java.lang.IllegalArgumentException: Failed to evaluate expression 'hasPermission(#eventId.id(), @eventTypePermission.target, @eventTypePermission.write())'
...
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1057E: No bean resolver registered in the context to resolve access to bean 'eventTypePermission'

深入研究它失败了,因为 SPeL 表达式使用的 bean 解析器实例为空。关于为什么会出现这种情况以及如何设置 bean 解析器以便它能够返回 EventTypePermission 的实例的任何想法?

我还尝试将@SpringBootTest(classes=EventUpdater.class) 更改为@SpringBootTest(classes=EventUpdater.class, EventTypePermission.class),但没有成功。

【问题讨论】:

【参考方案1】:

通过在配置类上显式设置应用程序上下文使其工作:

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
class TestConfig extends GlobalMethodSecurityConfiguration 
  private ApplicationContext applicationContext;

  @Autowired
  public TestConfig(ApplicationContext applicationContext) 
    this.applicationContext = applicationContext;
  


  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() 
    DefaultMethodSecurityExpressionHandler expressionHandler =
      new DefaultMethodSecurityExpressionHandler();
    expressionHandler.setPermissionEvaluator(new PermissionEvaluatorProxyTest());
    expressionHandler.setApplicationContext(applicationContext);
    return expressionHandler;
  

还必须更新SpringTestClass 注释以包含EventTypePermission@SpringBootTest(classes=EventUpdater.class, EventTypePermission.class),而不是在 TestConfig 类中定义 Bean。

【讨论】:

以上是关于使用自定义 bean 测试 PreAuthorize 注释的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring Boot MVC 测试中自定义 bean

如何在 Spring Security Bean 中模拟自定义 UserService 详细信息以进行单元测试?

mocking bean 并不总是有效

spring:自定义限定符注解@interface, 首选bean

spring data jpa自定义bean字段映射

Spring 实现自定义 bean 的扩展