SecurityContextHolder返回错误的用户名

问题描述 投票:0回答:1

虽然使用MockMVC测试REST端点,但Spring SecurityContextHolder偶尔会在同一测试中返回错误的用户名。我有一项服务,其中有一种返回用户名的方法,并且JPA存储库检查用户是否存在(释义):

String username = SecurityContextHolder.getContext().getAuthentication().getName();
Optional<RemoteUser> foundUser = userRepository.findOneByUsername(username);

测试在必要时用@WithMockUser(username = "..." roles="...")注释。之后,每个测试都将被拆除,并刷新Spring应用程序上下文。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = IntegrationTestApplication.class)
public abstract class IntegrationTest {

    ...


    @Resource
    private WebApplicationContext applicationContext;

    protected MockMvc mockMvc;

    ...


    @Before
    public void setUp() {
         mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
    }

    ...
}

大约有97-98%的时间,测试运行良好。但是,有时,Authentication对象返回的用户名不是@WithMockUser注释中定义的用户名。使用日志语句,我什至看到其他测试类已经运行中使用的用户名。在每次测试之前,都要设置数据库用户,因此,如果返回的用户名不在数据库中,则测试将失败,具体取决于需要用户的情况。

甚至更陌生的是,在测试过程中可以多次调用此服务中的此方法,有时用户名正确,然后突然之间是错误的。我不知道这怎么可能。我的理解是SecurityContextHolder bean是线程安全的,因此测试的脆弱性使我感到困惑。这怎么可能发生?

还有一些其他注意事项:

  1. 使用Spring Boot 2.2.7
  2. 测试不能并行运行。
  3. 上述服务总是通过场注入(@Lazy)延迟注入的。我不知道这是否是一个重要的细节,但这不是我的代码,这是唯一使用此批注的服务,为什么我不知道这样做的原因。
  4. [有些方法带有@Async注释,我以为可能会有副作用,但是我对此无能为力,而且这种思路纯属pit人。
  5. 我调试了TestSecurityContextHolder,并且看到确实调用了清除上下文的方法。
  6. 我也确实看到了这个SecurityContextHolder gives wrong User details,但是,一个,这个问题没有答案,第二,我的测试用例中没有并发请求。
spring-security integration-testing mockmvc
1个回答
0
投票

在进一步研究了SecurityContextHolder bean之后,我发现了有关线程可用的不同策略的更多信息。我发现一个设置了这个的bean:

SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

这会将安全上下文传播到从主线程派生的所有线程。它应该从测试上下文中排除,但不是。从测试应用程序上下文中删除此bean之后,以前只是间歇性失败的一个测试用例现在每次都失败。

长话短说,在[[parallel stream内部多次调用了利用SecurityContextHolder对象的服务方法。那就是这里:Null principal when get security context in parallelStream。重构它以仅在流外部一次调用它似乎已经解决了问题。

© www.soinside.com 2019 - 2024. All rights reserved.