0

Я нашел «The Login Page: Angular JS and Spring Security» большую помощь, чтобы добавить защиту CSRF в веб-приложение, которое использует Spring (Security, среди прочих), реализуя REST API, который используется Приложение AngularJS. Приложение AngularJS использует аутентификацию на основе форм (JSESSIONID). Вот суть этой работы:Получение теста RestAssured для работы с Spring Security CSRF для AngularJS

  • Скрытое поле ввода _csrf на странице входа
  • CsrfHeaderFilter добавляет XSRF-TOKEN печенье в ответ
  • HttpSessionCsrfTokenRepository с setHeaderName("X-XSRF-TOKEN")

Это все работает отлично (насколько я могу заметить).

Я считаю, что это приводит к следующему потоку:

  1. Перенаправление на страницу входа
  2. Войти страница содержит _csrf
  3. Логин ответ включает XSRF-TOKEN печенье
  4. Угловая использует значение куки в качестве X-XSRF-TOKEN заголовка на REST запросы
  5. Счастье!

Номер 2. кажется ванили Весна Безопасность 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) 

Ваша помощь очень признателен!

+0

Я думаю, вам нужно использовать RequestSpecification в RestAssured http://static.javadoc.io/com.jayway.restassured/rest-assured/2.4.1/com/jayway/restassured/specification/RequestSpecification.html –

+0

Спасибо для этого, но я боюсь, что вы не оцениваете, что я довольно опытный пользователь RestAssured, который приложил много усилий, прежде чем задавать этот вопрос, поэтому просто указывая на самый основной класс (интерфейс) в указанной библиотеке не помогает мне, если я не пропущу что-то здесь. В любом случае, немного более подробная информация поможет вам, спасибо! –

ответ

0

Возможно, вы захотите использовать заголовок, ответ res = given(). Header («X-Auth-Token», токен) .contentType («application/json») ...... У меня была аналогичная проблема с передать X-Auth Token, это сработало для меня.

1

Вы можете получить тест csrf, работающий с RestAssured, если следовать этому шаблону, изложенному на форуме google.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". Если ваш sessionId имеет другую метку, вы можете изменить это, настроив RestAssured на поиск этой метки.

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

Заменить «СЕССИЯ» на то, что обозначено вашим sessionId.