Java Spring 集成测试:使用 Oauth2 模拟 BearerTokenAuthentication

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

在我的 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 或其他网站上的文章到目前为止无法帮助我。

也许有人注意到一个关键错误或我遗漏的东西。 我将不胜感激。

java spring spring-boot jwt
1个回答
0
投票

RTFM

库自述文件明确指出任何自定义授权转换器都应公开为

@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 解码器的原因是什么?这看起来很不安全...

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