在我的 Web 层上运行集成测试时遇到问题。
我想模拟这个端点:
public ResponseEntity<List<Book>> getBooks(BearerTokenAuthentication auth, @AuthenticationPrincipal OidcUser principal) {...}
所以本质上它需要一个不记名令牌和一个可选的委托人,查找书籍并返回它们。
我还对安全过滤器链进行了以下配置:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(CsrfConfigurer::disable)
.authorizeHttpRequests((authz) ->
authz.anyRequest().authenticated()
).oauth2ResourceServer(oauth2 -> oauth2.jwt().decoder(jwtDecoder()).jwtAuthenticationConverter(new JwtBearerTokenAuthenticationConverter()));
return http.build();
}
public JwtDecoder jwtDecoder() {
return (token) -> {
JWT jwt = null;
try {
jwt = JWTParser.parse(token);
Map<String, Object> headers = new LinkedHashMap<>(jwt.getHeader().toJSONObject());
Map<String, Object> claims = jwt.getJWTClaimsSet().getClaims();
return new Jwt(token, null, null, headers, claims);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
};
}
所以本质上我收到了一个 jwt,并将其转换为一个身份验证对象,以将其放入 springs 安全上下文中。
现在我有点麻烦的是测试它。 我使用 c4_soft.springaddons @WithJwt() 来模拟 jwt,其中我传递了一些带有模拟值的标准 jwt。 这就是我目前的测试:
@SpringBootTest
@AutoConfigureMockMvc
public class BookApiTest {
private final MockMvc mockMvc;
@MockBean
FeignClient feignClient;
@MockBean
JwtDecoder jwtDecoder;
@Autowired
BookApiTest(MockMvc mockMvc) {
this.mockMvc = mockMvc;
}
@Test
@WithJwt("jwt.json")
public void testGetBooks() throws Exception {
ResponseEntity<List<Book>> responseEntity = ResponseEntity.ok(List.of(sampleBooks));
when(feignClient.getBooks()).thenReturn(responseEntity);
this.mockMvc.perform(MockMvcRequestBuilders.get("/books")
.with(SecurityMockMvcRequestPostProcessors.csrf()))
.andExpect(status().isOk());
}
}
现在我的问题是,无论何时。我在此设置中运行测试,我得到: jakarta.servlet.ServletException:请求处理失败:java.lang.IllegalStateException:当前用户主体不是类型 [org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication]:JwtAuthenticationToken [Principal=org.springframework.security .oauth2.jwt.Jwt@a795d843,凭据=[受保护],已验证=true,详细信息=null,授予权限=[]]
这就是让我恼火的地方。 假设我有一个有效的 jwt 并且我使用上面的配置使用 @autoconfiguremvc,我希望 jwt 能够自动转换为身份验证对象并提供给安全上下文。 为什么测试时失败,我不明白。
Spring 文档、SO 或其他网站上的文章到目前为止无法帮助我。
也许有人注意到一个关键错误或我遗漏的东西。 我将不胜感激。
库自述文件明确指出任何自定义授权转换器都应公开为
@Bean
。
如果身份验证转换器未作为 bean 公开,则测试身份验证工厂无法知道要构建什么
Authentication
实例,也不知道如何构建。
// Expose the authorization converter as @Bean
// An alternative is to decorate its class with @Component
@Bean
JwtBearerTokenAuthenticationConverter authenticationConverter() {
return new JwtBearerTokenAuthenticationConverter();
}
@Bean
public SecurityFilterChain filterChain(
HttpSecurity http,
// Inject the @Bean in the filter-chain
Converter<Jwt, AbstractAuthenticationToken> authenticationConverter) throws Exception {
http
.csrf(CsrfConfigurer::disable)
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.jwt()
.decoder(jwtDecoder())
.jwtAuthenticationConverter(authenticationConverter));
return http.build();
}
您最好坚持默认值
JwtAuthenticationConverter
。来自 JwtBearerTokenAuthenticationConverter
Javadoc:
此实现并不是要配置的,因为它只是一个适配器。
使用不验证
iss
、iat
或 exp
声明的解码器覆盖默认 JWT 解码器的原因是什么?这看起来很不安全...