使用 WebTestClient 在 @SpringBootTest 中模拟 JWT 令牌
Mocking JWT token in @SpringBootTest with WebTestClient
我在 Spring 引导项目中有以下控制器 class:
@GetMapping
public ResponseEntity<UserResponse> getUser(@AuthenticationPrincipal CustomUserDetails userDetails) {
try {
final UserResponse userData = userService.getUser(userDetails.getId());
return ResponseEntity.ok(userData);
} catch (UserNotFoundException e) {
log.error("User with id {} not found", userDetails.getId());
return ResponseEntity.notFound().build();
}
}
仅当客户端使用 Authorization: Bearer <token>
发送 JWT 令牌时才能访问此资源。在通过 JwtRequestFilter
.
解析 JWT 令牌后,CustomUserDetails
由 CustomUserDetailsService
提供
现在我想编写一个 @SpringBootTest
,它使用调用此资源的真实 HTTP 客户端。我的第一个想法是使用 MockMvc
对象,但后来我读到了 Spring.
提供的 WebTestClient
但是我还不明白如何模拟 JWT 令牌。这是我的初步尝试:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class UserControllerIT {
@Autowired
private ApplicationContext context;
private WebTestClient webTestClient;
@MockBean
private UserRepo userRepo;
@BeforeEach
public void setup() {
webTestClient = WebTestClient
.bindToApplicationContext(context)
.apply(springSecurity())
.configureClient()
.build();
}
@Test
@WithMockUser
public void someTest() {
final User user = createUser("foo@bar.com", "my-password");
when(userRepo.findById(anyLong())).thenReturn(Optional.of(user));
webTestClient
.mutateWith(mockJwt())
.get()
.uri("/user")
.header(ACCEPT, APPLICATION_JSON_VALUE)
.exchange()
.expectStatus().is2xxSuccessful();
}
此测试失败,出现以下异常:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'webHandler' available
但是我不确定我的方法是否有意义。 WebTestClient
是“正确”的方式吗?或者我需要使用 WebClient
?
我的安全配置:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtRequestFilter jwtRequestFilter;
public SecurityConfiguration(JwtRequestFilter jwtRequestFilter) {
this.jwtRequestFilter = jwtRequestFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(final HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.mvcMatcher("/services/**").authorizeRequests()
.mvcMatchers(PUBLIC_RESOURCES).permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
WebTestClient
是 TestRestTemplate
的推荐替代品。
在 https://github.com/spring-projects/spring-security 深入研究 Spring 安全源,展示了一些将 WebTestClient
与 JWT 结合使用的示例。例如:ServerOAuth2ResourceServerApplicationITests
鉴于您有一个服务 JwtTokenProvider
负责生成 JWT 令牌,测试可能如下所示。或者,如果可能,您可以使用 ServerOAuth2ResourceServerApplicationITests
.
中的常量标记
package no.yourcompany.yourapp.yourpackage;
import no.yourcompany.yourapp.configuration.JwtTokenProvider;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class YourIntegrationTest {
@Autowired
WebTestClient webTestClient;
@Autowired
JwtTokenProvider jwtTokenProvider;
@Test
public void postTest_withValidToken_receiveOk() {
var tokenString = jwtTokenProvider.createToken(
new UsernamePasswordAuthenticationToken("test-user", "P4ssword"));
webTestClient
.post().uri("/test")
.headers(http -> http.setBearerAuth(tokenString))
.exchange()
.expectStatus().isOk();
}
}
对于 WebTestClient
,将其添加到 POM
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
我在 Spring 引导项目中有以下控制器 class:
@GetMapping
public ResponseEntity<UserResponse> getUser(@AuthenticationPrincipal CustomUserDetails userDetails) {
try {
final UserResponse userData = userService.getUser(userDetails.getId());
return ResponseEntity.ok(userData);
} catch (UserNotFoundException e) {
log.error("User with id {} not found", userDetails.getId());
return ResponseEntity.notFound().build();
}
}
仅当客户端使用 Authorization: Bearer <token>
发送 JWT 令牌时才能访问此资源。在通过 JwtRequestFilter
.
CustomUserDetails
由 CustomUserDetailsService
提供
现在我想编写一个 @SpringBootTest
,它使用调用此资源的真实 HTTP 客户端。我的第一个想法是使用 MockMvc
对象,但后来我读到了 Spring.
WebTestClient
但是我还不明白如何模拟 JWT 令牌。这是我的初步尝试:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class UserControllerIT {
@Autowired
private ApplicationContext context;
private WebTestClient webTestClient;
@MockBean
private UserRepo userRepo;
@BeforeEach
public void setup() {
webTestClient = WebTestClient
.bindToApplicationContext(context)
.apply(springSecurity())
.configureClient()
.build();
}
@Test
@WithMockUser
public void someTest() {
final User user = createUser("foo@bar.com", "my-password");
when(userRepo.findById(anyLong())).thenReturn(Optional.of(user));
webTestClient
.mutateWith(mockJwt())
.get()
.uri("/user")
.header(ACCEPT, APPLICATION_JSON_VALUE)
.exchange()
.expectStatus().is2xxSuccessful();
}
此测试失败,出现以下异常:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'webHandler' available
但是我不确定我的方法是否有意义。 WebTestClient
是“正确”的方式吗?或者我需要使用 WebClient
?
我的安全配置:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtRequestFilter jwtRequestFilter;
public SecurityConfiguration(JwtRequestFilter jwtRequestFilter) {
this.jwtRequestFilter = jwtRequestFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(final HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.mvcMatcher("/services/**").authorizeRequests()
.mvcMatchers(PUBLIC_RESOURCES).permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
WebTestClient
是 TestRestTemplate
的推荐替代品。
在 https://github.com/spring-projects/spring-security 深入研究 Spring 安全源,展示了一些将 WebTestClient
与 JWT 结合使用的示例。例如:ServerOAuth2ResourceServerApplicationITests
鉴于您有一个服务 JwtTokenProvider
负责生成 JWT 令牌,测试可能如下所示。或者,如果可能,您可以使用 ServerOAuth2ResourceServerApplicationITests
.
package no.yourcompany.yourapp.yourpackage;
import no.yourcompany.yourapp.configuration.JwtTokenProvider;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class YourIntegrationTest {
@Autowired
WebTestClient webTestClient;
@Autowired
JwtTokenProvider jwtTokenProvider;
@Test
public void postTest_withValidToken_receiveOk() {
var tokenString = jwtTokenProvider.createToken(
new UsernamePasswordAuthenticationToken("test-user", "P4ssword"));
webTestClient
.post().uri("/test")
.headers(http -> http.setBearerAuth(tokenString))
.exchange()
.expectStatus().isOk();
}
}
对于 WebTestClient
,将其添加到 POM
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>