我找到了" The Login Page: Angular JS and Spring Security"很有帮助,将CSRF保护添加到一个Web应用程序中,该应用程序使用Spring(安全等)实现AngularJS应用程序使用的REST API。 AngularJS应用程序使用基于表单的登录(JSESSIONID
)进行身份验证。这是这项工作的精髓所在:
_csrf
CsrfHeaderFilter
将XSRF-TOKEN
Cookie添加到回复HttpSessionCsrfTokenRepository
与setHeaderName("X-XSRF-TOKEN")
这个一切正常(据我所知)。
我相信这会导致以下流程:
_csrf
XSRF-TOKEN
Cookie X-XSRF-TOKEN
标头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)
非常感谢您的帮助!
答案 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,它对我有用。