spring-security - SecurityContextHolder 返回错误的用户名
问题描述
在使用 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 是线程安全的,因此测试的脆弱性让我感到困惑。这怎么可能发生?
只是需要注意的其他一些事项:
- 使用 Spring Boot 2.2.7
- 测试不是并行运行的。
- 上述服务总是通过字段注入 (
@Lazy
) 延迟注入。我不知道这是否是一个重要的细节,但这不是我的代码,这是唯一使用此注释的服务,我不知道为什么这样做。 - 有一些方法注释了
@Async
,我认为可能会有副作用,但我没有任何支持,而且这种思路纯粹是吐槽。 - 我已经调试了 TestSecurityContextHolder 并且已经看到确实调用了清除上下文的方法。
- 我确实看到这个SecurityContextHolder 也给出了错误的用户详细信息,但一是没有答案,二是我的测试用例中没有并发请求。
解决方案
在进一步研究了 SecurityContextHolder bean 之后,我发现了一些关于线程可用的不同策略的更多信息。我发现了一个设置这个的bean:
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
这会将安全上下文传播到从主线程产生的所有线程。它应该被排除在测试上下文之外,但事实并非如此。在从测试应用程序上下文中删除这个 bean 之后,一个之前只是间歇性失败的测试用例现在每次都失败。
长话短说,利用 SecurityContextHolder 对象的服务方法在并行流中被多次调用。这把我带到了这里:在 parallelStream 中获取安全上下文时为 Null 主体。将其重构为仅在流之外调用一次似乎已经解决了这个问题。
推荐阅读
- angular - 模板解析错误:“mat-icon”不是已知元素
- html - `overflow-y: auto` 忽略父母的身高
- php - CORS - jQuery 获取 ajax 响应状态失败
- html - 如何在网站上添加两种语言?
- php - PHP登录系统不会登录所有页面和页面脚本停止执行
- makefile - 在我运行“./configure”命令之前,如何重置源代码树成为他们喜欢的样子?
- git - 如何忽略我没有写入权限的模块中的更改?
- qt - 如何使用 qt 安装程序框架在卸载期间删除/清除 AppData/Roaming/MyFolder 文件?
- angular - 时间用完Angular5时发送事件
- android - 获取图像单色相机华为P20