【问题标题】:How to access a CSRF token in a Spring controller unit test?如何在 Spring 控制器单元测试中访问 CSRF 令牌?
【发布时间】:2016-10-29 19:38:43
【问题描述】:

我有一个 Spring 控制器,它在来自“/user”的经过身份验证的 GET 后返回以下 JSON:

{"name":<name>,"token":<csrf-token>}

我尝试为控制器构建一个单元测试,以验证返回的 JSON 是否包含动态生成的 CSRF 令牌:

@Autowired
private FilterChainProxy springSecurityFilterChain;

private MockMvc mvc;

@Before
public void setUp()
    throws Exception
{
    ...
    mvc = standaloneSetup(controller)
        .apply(springSecurity(springSecurityFilterChain))
        .build();
}

@Test
public void getUser()
    throws Exception
{
    CsrfRequestPostProcessor csrfPostProcessor = null;
    mvc.perform(get("/user").with(user(Const.USER)).with(csrfPostProcessor = csrf()))
        .andExpect(status().isOk())
        .andExpect(content().json("{\"name\":\"" + Const.NAME + "\",\"token\":\"" + csrfPostProcessor.toString() + "\"}"));
}

测试失败如下:

Failed tests: 
  ControllerTest.getUser:74 token
Expected: org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors$CsrfRequestPostProcessor@203c20cf
     got: 565a95b0-d0bb-4376-a8a0-725a3b16a787

有没有办法解决这个问题,如果没有,有没有办法构建一个使用动态生成的 CSRF 令牌的替代测试?

【问题讨论】:

  • 这里生成令牌是没有用的,令牌会随着对服务器的新请求随时更改。
  • 不是没用的。控制器在会话期间返回动态生成的令牌以供其他(非 Spring)服务使用。单元测试模拟了这样一个会话。

标签: json spring unit-testing csrf spring-mvc-test


【解决方案1】:

这是一种可能的解决方案——构建一个自定义匹配器,以截取 UUID4 格式的 CSRF 令牌:

private static Pattern CSRF_PATTERN = Pattern.compile("[\\da-f]{8}-[\\da-f]{4}-4[\\da-f]{3}-[\\da-f]{4}-[\\da-f]{12}");

public static Matcher<String> isCsrf(StringBuilder intercept) {
    return new ArgumentMatcher<String>() {
        @Override
        public boolean matches(Object obj) {
            // intercept may be null
            Assert.isTrue(obj instanceof DefaultCsrfToken, "obj");

            String token = ((DefaultCsrfToken)obj).getToken();
            if (intercept != null) {
                intercept.setLength(0);
                intercept.append(token);
            }

            java.util.regex.Matcher matcher = CSRF_PATTERN.matcher(token);
            return matcher.matches();
        }           
    };
}

然后按如下方式使用:

@Test
public void getUser()
    throws Exception
{
    StringBuilder token = new StringBuilder();
    mvc.perform(get("/user").with(user(Const.USER)).with(csrf()))
        .andExpect(status().isOk())
        .andExpect(request().attribute("_csrf", isCsrf(csrf)))
        .andExpect(content().json("{\"name\":\"" + Const.NAME + "\",\"token\":\"" + token.toString() + "\"}"));
}

作为奖励,您将获得一个 CSRF 令牌验证器。

【讨论】:

    猜你喜欢
    • 2017-04-06
    • 2014-10-25
    • 2022-09-27
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    • 2014-08-20
    相关资源
    最近更新 更多