【问题标题】:Spring Data + SpringBootTest: How to mock SecurityContext for JPA auditing?Spring Data + SpringBootTest:如何模拟 SecurityContext 进行 JPA 审计?
【发布时间】:2019-08-08 06:59:52
【问题描述】:

在 Spring Boot 应用程序中,我想测试 (JUnit 5) 启用审计 (@EnableJpaAuditing) 的持久层。 我使用 Liquibase 设置 H2 db 和 Hibernate 作为 JPA 实现。

@Configuration
//@EnableTransactionManagement
@EnableJpaAuditing
//@EnableJpaRepositories
public class MyPersistenceConfig {
}

我的实体有以下字段:

@CreatedDate
@Column(name = "CREATED_AT", updatable = false)
private Instant createdAt;

@CreatedBy
@Column(name = "CREATED_BY", updatable = false)
private String createdBy;

@CreatedDate
@Column(name = "LAST_MODIFIED_AT")
private Instant lastModifiedAt;

@CreatedBy
@Column(name = "LAST_MODIFIED_BY")
private String lastModifiedBy;

我有以下依赖项:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-core</artifactId>
        <scope>runtime</scope>
        <!--<scope>test</scope>-->
    </dependency>

    <!-- Testing -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>

我尝试了几种注释组合:

@SpringBootTest //(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@DataJpaTest
@ContextConfiguration(classes = MyPersistenceConfig.class)
@EnableAutoConfiguration
//@SecurityTestExecutionListeners
//@Import(SpringBootWebSecurityConfiguration.class)
@WithMockUser(username = "test", password = "test", roles = "USER")
@ExtendWith(SpringExtension.class)
class MyRepositoryTest {

    @Autowired
    private MyRepository testee;

...
}

但无论我尝试什么,要么存储库为空(自动装配),要么在插入条目时出现异常:

NULL not allowed for column "CREATED_BY"; SQL statement:

我想我需要一个 SecurityContext(目前不能用于自动装配)。

提供与审计和 @WithMockUser 一起使用的模拟 SecurityContext 的最简单方法是什么?

【问题讨论】:

  • @WithMockUser 以UsernamePasswordAuthenticationToken 作为主体模拟SecurityContext。我不确定您为什么会收到 NULL now allowed... 错误。您可以通过 GitHub 存储库分享您的示例吗?

标签: java spring-boot spring-security spring-data-jpa spring-test


【解决方案1】:

这是一个老问题,但对于那些试图让 Spring Data 审计在他们的集成测试中工作时偶然发现它的人来说,这可能会有所帮助。审计功能需要一个AuditingAware bean 来获取当前用户。在DataJpaTest 中,这似乎不见了。使其可用的一种方法是将@Bean 配置添加到您的测试中。

@RunWith(SpringRunner.class)
@DataJpaTest
@Import({DatabaseIntegrationTest.TestConfig.class})
@WithMockUser
class DatabaseIntegrationTest {

  @TestConfiguration
  @RequiredArgsConstructor
  static class TestConfig {
    @Bean
    public AuditorAware<String> auditorAware() {
      return () -> Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication().getName());
    }
  }
}

【讨论】:

  • 我还必须在测试类中添加@EnableJpaAuditing
【解决方案2】:
@Before
public void setup() {

    User user = userService.findByEmail("umanking@gmail.com").get();

    SecurityContextHolder.getContext().setAuthentication(new Authentication() {
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return null;
        }

        @Override
        public Object getCredentials() {
            return user.getPassword();
        }

        @Override
        public Object getDetails() {
            return user;
        }

        @Override
        public Object getPrincipal() {
            return null;
        }

        @Override
        public boolean isAuthenticated() {
            return true;
        }

        @Override
        public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {

        }

        @Override
        public String getName() {
            return user.getName();
        }
    });

}

也许你可以使用@Before注解

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-09
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 2015-01-20
    • 1970-01-01
    • 2017-05-22
    • 2019-11-17
    相关资源
    最近更新 更多