模拟身份验证 Spring Security

14

我有一个使用了@Secured注解的存储库方法。我正在尝试为此方法编写单元测试,但是我的测试失败了,因为我需要认证才能调用该方法。方法本身恰好是save()方法。当我调用该方法时,我得到的错误信息是:

 org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext

我无法测试这个方法,因为它需要验证,而且我无法保存用户进行身份验证(我正在使用hsqldb),因为我需要调用这个方法才能保存。有没有关于如何对带有@secured注释的方法进行单元测试或如何模拟身份验证的建议。

3个回答

15

这取决于您想要测试什么。

  • 如果您想在没有任何安全设置的情况下测试应用程序的业务逻辑,可以完全禁用安全性。假设您使用SpringJunit4Runner,则可以将安全配置放入单独的文件中,并将其不包括在@ContextConfiguration中。

  • 如果您希望测试授权(例如正确工作的@Secured注释),可以直接访问SecurityContext,绕过所有与身份验证相关的内容(在这种情况下,您也可以将身份验证配置放入单独的文件中并且不加载它):

    您所需要做的就是将适当的Authentication对象放入SecurityContextHolder中:

@Test
public void myTest() {
    login(...);
    ...
    logout();
}

private void login(...) {
    SecurityContextHolder.getContext().setAuthentication(
        new UsernamePasswordAuthenticationToken(...)
    )
}

private void logout() {
    SecurityContextHolder.clearContext();
}
  • 最后,如果你想测试身份验证,可以使用包含测试数据的数据库运行测试。


  • 很遗憾,我无法做到这一点。我的身份验证管理器是一个自定义的userDetailService,它使用我的存储库从数据库中获取用户。因此,如果我无法将用户保存到持久性存储中,则userDetailsService将无法找到该用户,从而导致BadCredentials异常。 - vikash dat

    5

    a) 这不是单元测试,而是集成测试。单元测试则不会有问题。

    b) 我建议您保持测试设置的分离。为什么不在您不测试安全相关功能的测试中停用Spring Security?

    c) 如果其他方法都失败了:注入一个 JdbcTemplate 对象,并在测试设置过程中手动创建用户数据以进行SQL测试。请参阅支持集成测试的类


    我不知道在运行时禁用安全性是可能的。您是否有资源链接可以展示如何禁用安全性? - vikash dat
    @vikashdat 另一种方法是不要激活它。当然,这取决于测试的类型。如果您正在针对已部署的服务器进行测试,则可能无法选择此选项。 - Sean Patrick Floyd
    我不确定我理解了...怎么可能不激活它?你的意思是不用SpringJunit4Runner运行测试吗?如果这样做,我将失去自动装配组件的能力。 - vikash dat
    1
    @vikashdat 使用SpringJunit4Runner,但是使用一个不了解Spring Security的Spring上下文。 - Sean Patrick Floyd
    它不应该有任何安全元素,特别是<global-method-security> - sourcedelica
    我尝试使用@Profiles来取消安全性,并使用一个带有Spring Security的子类和另一个不带Spring Security的子类。想法是只在生产中在Web应用程序servlet中加载Spring Security。但现在这并不起作用 - Spring Security与servlet 3 java config + profiles不兼容,您必须将所有java config保留在单个类中。花了很多时间摸索解决方法 - 我认为为集成测试创建login()/logout()方法更好 - 可以明确地看到什么受到保护,什么没有受到保护... - user1016765

    1

    我会考虑使用注释,比如@WithMockUser来填充测试安全上下文中的“角色”(测试用户具有特定权限,如'admin'、'salesman'等)。

    如果存储库上有@Secured,那么它一定是因为规范声明了“用户必须拥有这个授权才能查询那个”,因此安全性也应该进行单元测试。

    如果UsernameAuthenticationToken@WithMockUser创建的内容)不满足您的需求,则可以选择其他注释,例如我编写的此链接,或者编写自己的注释以创建您想要的准确的Authentication实现。

    Libs 这里, 从那里提取的示例结果

    @RunWith(SpringRunner.class)
    @Import(JwtAuthenticationTokenMessageServiceTests.TestConfig.class)
    public class JwtAuthenticationTokenMessageServiceTests {
    
        @Autowired
        private MessageService<JwtAuthenticationToken> messageService;
    
        @Test
        @WithMockAuthentication(authType = JwtAuthenticationToken.class, authorities = "ROLE_AUTHORIZED_PERSONNEL")
        public void secretWithScopeAuthorizedPersonnelAuthority() {
            assertThat(messageService.getSecret()).isEqualTo("Secret message");
        }
    
        @TestConfiguration
        @EnableGlobalMethodSecurity(prePostEnabled = true)
        @Import({ JwtTestConf.class, JwtAuthenticationTokenServletApp.JwtAuthenticationTokenMessageService.class })
        public static class TestConfig {
        }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接