【问题标题】:Getting RestAssured test to work with Spring Security CSRF for AngularJS让 RestAssured 测试与 AngularJS 的 Spring Security CSRF 一起工作
【发布时间】:2016-09-24 04:37:49
【问题描述】:

我发现“The Login Page: Angular JS and Spring Security”非常有帮助,可以将 CSRF 保护添加到使用 Spring(安全等)实现 AngularJS 应用程序使用的 REST API 的 Web 应用程序中。 AngularJS 应用程序使用基于表单的登录 (JSESSIONID) 进行身份验证。这是这项工作的精髓:

  • 登录页面隐藏输入栏_csrf
  • CsrfHeaderFilterXSRF-TOKEN cookie 添加到响应中
  • HttpSessionCsrfTokenRepositorysetHeaderName("X-XSRF-TOKEN")

一切正常(据我观察)。

我相信这会导致以下流程:

  1. 重定向到登录页面
  2. 登录页面包含_csrf
  3. 登录响应包含XSRF-TOKENcookie
  4. Angular 使用 cookie 值作为 REST 请求的 X-XSRF-TOKEN 标头
  5. 幸福!

第 2 位似乎是普通的 Spring Security CSRF。 3.和4.似乎an attempt适应AngularJS CSRF support

同样,这一切都可以正常工作,从测试登台实例开始。但是,一些集成测试被破坏,特别是使用 RestAssured 的测试。我还没有找到这种设置的 RestAssured 的好例子。 This 是我能找到的所有文档。

它建议使用formAuthConfig().withAutoDetectionOfCsrf()withCsrfFieldName("_csrf"),并在登录页面上做一个显式的get。使用它似乎处理登录罚款。但我不明白我如何告诉 RestAssured 转向使用 X-XSRF-TOKEN 标头。

我没有成功地完成了这段代码的很多变体,但我目前有这个:

    FormAuthConfig baseConfig = new FormAuthConfig(loginPage, "username", "password");
    FormAuthConfig config = baseConfig.sendCsrfTokenAsHeader();
    config = config.withCsrfFieldName("X-XSRF-TOKEN");
    RestAssured.authentication = RestAssured.form(userName, password, config);

    given().auth().form("admin", "admin", baseConfig.withCsrfFieldName("_csrf"))
        .when().get(loginPage)
        .then().statusCode(200);

    expect().statusCode(200)
        .when().get(...); // line 246

这将获得以下输出:

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Error 401 Unauthorized</title>
  </head>
  <body>
    <h2>HTTP ERROR 401</h2>
    <p>Problem accessing /application_name/rest/something. Reason:
</p>
    <pre>    Unauthorized</pre>
    <hr/>
    <i>
      <small>Powered by Jetty://</small>
    </i>
    <hr/>
  </body>
</html>

java.lang.IllegalArgumentException: Couldn't find the CSRF input field with name X-XSRF-TOKEN in response. Response was:
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Error 401 Unauthorized</title>
  </head>
  <body>
    <h2>HTTP ERROR 401</h2>
    <p>Problem accessing /application_name/rest/something. Reason:
</p>
    <pre>    Unauthorized</pre>
    <hr/>
    <i>
      <small>Powered by Jetty://</small>
    </i>
    <hr/>
  </body>
</html>

    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:80)
    at org.codehaus.groovy.reflection.CachedConstructor.doConstructorInvoke(CachedConstructor.java:74)
    at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrap.callConstructor(ConstructorSite.java:84)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
    at com.jayway.restassured.internal.filter.FormAuthFilter.filter(FormAuthFilter.groovy:85)
    at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
    at com.jayway.restassured.internal.filter.FilterContextImpl.next(FilterContextImpl.groovy:49)
    at com.jayway.restassured.filter.FilterContext$next.call(Unknown Source)
    at com.jayway.restassured.internal.RequestSpecificationImpl.invokeFilterChain(RequestSpecificationImpl.groovy:994)
<snip>
    at com.jayway.restassured.internal.ResponseSpecificationImpl.get(ResponseSpecificationImpl.groovy)
    at com.company.application_name.AbstractJettyJsonRestTest.getCollection(AbstractJettyJsonRestTest.java:246)
    at com.company.application_name.JsonRestIntegrationTest.testSomething(JsonRestIntegrationTest.java:74)
<snip>
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)

非常感谢您的帮助!

【问题讨论】:

  • 我认为您需要在 RestAssured static.javadoc.io/com.jayway.restassured/rest-assured/2.4.1/com/… 中使用 RequestSpecification@
  • 谢谢你,但恐怕你不明白我是一个非常有经验的 RestAssured 用户,在问这个问题之前付出了很多努力,所以只是指出我所述库中最核心的类(接口)对我没有帮助,除非我在这里遗漏了一些东西。无论如何,再详细一点会有所帮助,谢谢!

标签: java angularjs spring-security csrf-protection rest-assured


【解决方案1】:

如果您遵循谷歌论坛上概述的这种模式,您可以使用 RestAssured 进行 csrf 测试。 https://groups.google.com/forum/#!topic/rest-assured/HidZCdNA4iA

基本上,您必须创建一个辅助方法来手动启动登录流程,而不是依靠 RestAssured 为您完成它。这是从其原始位置复制以将其保存在 SO 上所需的代码。

protected SessionData login(String username, String password) {
    Response getLoginResponse =
        given().
            filter(sessionFilter).
        when().
            get("/login.html").
        then().
            extract().response();

    String token = getLoginResponse.header("XSRF-TOKEN");

    RestAssured.given().log().all().
        filter(sessionFilter)
        .header("X-XSRF-TOKEN", csrfToken)
        .param("username", username)
        .param("password", password)
        .when()
        .post("/login");


    Response tokenResponse =
        given().log().all().
            filter(sessionFilter).
        when().

            get("/token").
        then().log().all().
            extract().response();

    return new SessionData(tokenResponse.header("XSRF-TOKEN"), sessionFilter.getSessionId());
}

login 方法本质上是导航到登录页面,获取 csrf 令牌,使用该令牌登录,然后再次调用以获取更新后的 sessionId 和 csrf 令牌,因为它们在登录后发生了变化。

Url 和 csrf 参数可能需要根据您的具体设置进行更改。

SessionData 只是一个自定义的辅助 POJO 来存储数据

private class SessionData {
    private String csrf;
    private String session;
}

然后在这样的测试中使用它

@Test
public void whenAdminAccessDiscoveryResource_thenSuccess() {
    SessionData sessionData = login("admin", "admin");
    final Response response = RestAssured.given()
        .header("X-XSRF-TOKEN", sessionData.getCsrf())
        .filter(sessionFilter)
        .get(ROOT_URI + "/discovery");
    Assert.assertEquals(HttpStatus.OK.value(), response.getStatusCode());
}

RestAssured 假定您的 sessionId 来自标有“JSESSIONID”的 cookie。如果您的 sessionId 有不同的标签,您可以通过配置 RestAssured 来查找该标签来更改它。

    RestAssured.config = config()
        .sessionConfig(new SessionConfig().sessionIdName("SESSION"));

将“SESSION”替换为您的 sessionId 标记。

【讨论】:

    【解决方案2】:

    您可能想使用标头,响应 res = given().header("X-Auth-Token",token).contentType("application/json")......我有类似的问题要通过X-Auth Token,它对我有用。

    【讨论】:

      猜你喜欢
      • 2023-03-18
      • 1970-01-01
      • 2013-12-13
      • 2019-05-26
      • 2016-01-11
      • 2015-03-06
      • 2018-05-25
      • 2018-12-12
      • 2020-07-29
      相关资源
      最近更新 更多