MockMVC - 如何使用 org.hamcrest.Matcher 在 spring 安全集成测试中检查 JWT 令牌的内容
MockMVC - How to check the content of a JWT token in a spring security integration test with org.hamcrest.Matcher
我正在获取 JWT 令牌作为来自 MockMvc 请求的响应。我要查看此回复的内容:
mockMvc.perform(post("/authorize")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeEmailAndPassword("test1@app.com", "1111"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.params(params)
.accept(MediaType.APPLICATION_JSON))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isOk())
;
结果将是:
{
"id_token": "............(long Base64 string)"
}
当我们使用 JWT.io 解码令牌时,我们看到:
{
"sub": "cc15a160-2d62-4091-b89a-117e77346a58",
"nbf": 1543846725,
"auth_level": "trusted",
"iss": "http://localhost:9090/",
"exp": 1543847724,
"iat": 1543846725,
"nonce": "random_string",
"jti": "64b8b6e3-5cd0-4242-bcea-2c5d498d64c1"
}
一切都很好,但我想做类似的事情:
.andExpect(jsonPath("$.id_token", Matchers.not(null)))
.andExpect(decodeJWT(jsonPath("$.id_token")).getValueOf("nonce"), Matchers.is("random_string"));
我该怎么做?
好吧,我自己找到了答案...基本上,使用 org.hamcrest.Matcher
是不可能的,但我们可以将响应分成几部分并将它们映射到 DTO。
首先,我做了一些状态和基本检查,然后 return 响应为 MvcResult
:
MvcResult result = mockMvc.perform(post("/authorize")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeEmailAndPassword("test1@app.com", "1111"))
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id_token", Matchers.notNullValue()))
.andReturn();
然后,我为 Jackson 反序列化创建了一些 DTO:(记得创建 class not 作为内部 class,因为 Jackson 会抱怨 "only can use no-arg constructor of inner non-static class")
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class TokenResponseDTO implements Serializable {
//@JsonProperty("id_token")
private String idToken;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class JWTPayloadDTO implements Serializable {
private String aud;
private String sub;
private String nbf;
private String authLevel;
private String iss;
private Long exp;
private Long iat;
private String nonce;
private String jti;
}
终于,JWT token解析比我想象的简单多了:
String token = mapper.readValue(result.getResponse().getContentAsString(), TokenResponseDTO.class).getIdToken();
JWSObject jwsObject = JWSObject.parse(token);
JWTPayloadDTO payload = mapper.readValue(jwsObject.getPayload().toString(), JWTPayloadDTO.class);
Assert.assertEquals("random_string", payload.getNonce());
... // other checks
我正在获取 JWT 令牌作为来自 MockMvc 请求的响应。我要查看此回复的内容:
mockMvc.perform(post("/authorize")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeEmailAndPassword("test1@app.com", "1111"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.params(params)
.accept(MediaType.APPLICATION_JSON))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isOk())
;
结果将是:
{
"id_token": "............(long Base64 string)"
}
当我们使用 JWT.io 解码令牌时,我们看到:
{
"sub": "cc15a160-2d62-4091-b89a-117e77346a58",
"nbf": 1543846725,
"auth_level": "trusted",
"iss": "http://localhost:9090/",
"exp": 1543847724,
"iat": 1543846725,
"nonce": "random_string",
"jti": "64b8b6e3-5cd0-4242-bcea-2c5d498d64c1"
}
一切都很好,但我想做类似的事情:
.andExpect(jsonPath("$.id_token", Matchers.not(null)))
.andExpect(decodeJWT(jsonPath("$.id_token")).getValueOf("nonce"), Matchers.is("random_string"));
我该怎么做?
好吧,我自己找到了答案...基本上,使用 org.hamcrest.Matcher
是不可能的,但我们可以将响应分成几部分并将它们映射到 DTO。
首先,我做了一些状态和基本检查,然后 return 响应为 MvcResult
:
MvcResult result = mockMvc.perform(post("/authorize")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeEmailAndPassword("test1@app.com", "1111"))
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id_token", Matchers.notNullValue()))
.andReturn();
然后,我为 Jackson 反序列化创建了一些 DTO:(记得创建 class not 作为内部 class,因为 Jackson 会抱怨 "only can use no-arg constructor of inner non-static class")
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class TokenResponseDTO implements Serializable {
//@JsonProperty("id_token")
private String idToken;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class JWTPayloadDTO implements Serializable {
private String aud;
private String sub;
private String nbf;
private String authLevel;
private String iss;
private Long exp;
private Long iat;
private String nonce;
private String jti;
}
终于,JWT token解析比我想象的简单多了:
String token = mapper.readValue(result.getResponse().getContentAsString(), TokenResponseDTO.class).getIdToken();
JWSObject jwsObject = JWSObject.parse(token);
JWTPayloadDTO payload = mapper.readValue(jwsObject.getPayload().toString(), JWTPayloadDTO.class);
Assert.assertEquals("random_string", payload.getNonce());
... // other checks