我有一个关于从一个服务中获取jwt令牌并在一个服务的测试方法中使用它的问题。
我试着写JUnit Controller test在支付,产品和订单服务在我的春季启动微服务的例子。在我定义了授权服务来处理创建用户和使用jwt登录之后令牌和定义api网关使用JWTFilter,需要定义承载令牌对于每个测试请求方法的每个请求
正常情况下,它的工作,但我必须得到jwt令牌和使用它,但我不知道如何得到它?
下面是PaymentControllerTest的一个测试方法。如您所见,没有授权承载者的定义。我怎么定义它呢?
@Test
@DisplayName("Place Order -- Success Scenario")
void test_When_placeOrder_DoPayment_Success() throws Exception {
OrderRequest orderRequest = getMockOrderRequest();
String jwt = getJWTTokenForRoleUser();
MvcResult mvcResult
= mockMvc.perform(MockMvcRequestBuilders.post("/order/placeorder")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer " + jwt)
.content(objectMapper.writeValueAsString(orderRequest)))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
String orderId = mvcResult.getResponse().getContentAsString();
Optional<Order> order = orderRepository.findById(Long.valueOf(orderId));
assertTrue(order.isPresent());
Order o = order.get();
assertEquals(Long.parseLong(orderId), o.getId());
assertEquals("PLACED", o.getOrderStatus());
assertEquals(orderRequest.getTotalAmount(), o.getAmount());
assertEquals(orderRequest.getQuantity(), o.getQuantity());
}
以下是关于jwt令牌处理的方法
private String getJWTTokenForRoleUser(){
var loginRequest = new LoginRequest("User1","user1");
String jwt = jwtUtils.generateJwtToken(loginRequest.getUsername());
return jwt;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginRequest {
private String username;
private String password;
}
下面是jwtil类
@Component
public class JwtUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtUtils.class);
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expireMs}")
private int jwtExpirationMs;
public String generateJwtToken(String username) {
return generateTokenFromUsername(username);
}
public String generateTokenFromUsername(String username) {
return Jwts.builder().setSubject(username).setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
.signWith(SignatureAlgorithm.HS512, jwtSecret).compact();
}
}
要运行应用程序,1)运行Service registry (Eureka Server)
2)运行config server
3)通过以下命令在docker
上运行zipkin和redisdocker run -d -p 9411:9411 openzipkin/zipkin
docker run -d --name redis -p 6379:6379 redis
4)运行api gateway
5)运行认证服务、产品服务、订单服务和支付服务
(我在application-test中定义了所有属性。但是无法从OrderControllerTest)
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'jwt.secret' in value "${jwt.secret}"
这是repo: Link
您可以创建一个UTIL并从那里获得一个令牌,并在您的测试用例中使用
您不能使用模拟的jwt编写服务间测试:jwt是签名的,并且您无法访问授权服务器的私钥。
要么写
- 在模拟HTTP环境中进行测试:
@webMvcTest
或@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
,在这种情况下,对其他服务的调用应该被模拟(使用wire mock或其他东西),因此将在测试安全上下文中进行身份验证(在这种情况下根本没有JWT) - "full"在所有服务启动的情况下进行集成测试,这是"真实的"。客户端(WebClient, RestTemplate, @FeignClient,或者用Protractor或其他东西操纵的实际UI)和真正的令牌(即由真实授权服务器颁发给真实用户)
第二种解决方案显然更复杂,学习、设置和维护的时间更长。此外,测试将变得更慢、更脆弱。广泛的"full"集成测试是一个地狱,经常被认为是最糟糕的实践。
您的项目甚至没有接近运行集成测试(没有父组件来管理模块间依赖关系和集成测试配置,错过配置的模块,…)
从单元测试开始:@WebMvcTest用于单个控制器,@MockBean用于使用@组件,如@Services和@Repository
然后编写范围为一个微服务的集成测试:@SpringBoot使用wiremock测试以模拟与其他微服务的交换。
也许只有这样,您才能编写包含几个微服务、一个授权服务器和一个"富"组件的完整集成测试。或REST客户端(如果您想模拟用户,则使用实用程序方法来执行授权代码流)。
现在,我强烈建议您关注前两种测试,遵循这些教程。同一个repo的示例还演示了如何使用安全表达式为@Services和@Repository编写单元测试。