获得RestAssured测试以使用Spring Security CSRF for AngularJS

时间:2016-05-26 09:08:38

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

我找到了" The Login Page: Angular JS and Spring Security"很有帮助,将CSRF保护添加到一个Web应用程序中,该应用程序使用Spring(安全等)实现AngularJS应用程序使用的REST API。 AngularJS应用程序使用基于表单的登录(JSESSIONID)进行身份验证。这是这项工作的精髓所在:

  • 登录页面上的隐藏输入字段_csrf
  • CsrfHeaderFilterXSRF-TOKEN Cookie添加到回复
  • HttpSessionCsrfTokenRepositorysetHeaderName("X-XSRF-TOKEN")

这个一切正常(据我所知)。

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

  1. 重定向到登录页面
  2. 登录页面包含_csrf
  3. 登录回复包括XSRF-TOKEN Cookie
  4. Angular在REST请求中将cookie值用作X-XSRF-TOKEN标头
  5. 快乐!
  6. 2号似乎是vanilla 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)
    

    非常感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

如果您遵循Google论坛中列出的此模式,您可以使用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());
}

登录方法实质上导航到登录页面,获取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。

答案 1 :(得分:0)

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