使用 CSRF 进行放心和基本身份验证的集成测试

Posted

技术标签:

【中文标题】使用 CSRF 进行放心和基本身份验证的集成测试【英文标题】:Integration test with rest assured and basic auth with CSRF 【发布时间】:2016-04-12 20:06:43 【问题描述】:

我有一个基于 https://spring.io/guides/tutorials/spring-security-and-angular-js/ 的带有 AngularJs 应用程序的 Spring Boot 工作

我正在使用基本身份验证,我想为它编写一个集成测试。目前,我总是收到带有消息的 403 状态代码:

Expected CSRF token not found. Has your session expired?

这是我的测试:

@Test
public void givenAdmin_deleteOfBookIsAllowed() 

    Response response = given().auth().preemptive().basic("admin", "admin").get("/api/user/");
    response.then().log().all();
    String sessionId = response.sessionId();
    String token = response.cookie("XSRF-TOKEN");

    Book book = Books.randomBook();
    bookRepository.save(book);

    given()
            .sessionId(sessionId)
            .cookie("XSRF-TOKEN", token)
            .header("X-XSRF-TOKEN", token)
            .pathParam("id", book.getId().getId())
            .when().delete("/api/books/id")
            .then().statusCode(HttpStatus.SC_NO_CONTENT);

我正在按照教程使用自定义令牌存储库:

 private CsrfTokenRepository csrfTokenRepository() 
    HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
    repository.setHeaderName("X-XSRF-TOKEN");
    return repository;

这是第一次调用的请求/响应:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=7020E58A8D6DC2C883FD5D6BD086512A; Path=/; HttpOnly
Set-Cookie: XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91; Path=/
X-Application-Context: application:staging:0
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Fri, 08 Jan 2016 07:43:21 GMT


    "details": 
        "remoteAddress": "127.0.0.1",
        "sessionId": "7020E58A8D6DC2C883FD5D6BD086512A"
    ,
    "authorities": [
        
            "authority": "ROLE_ADMIN"
        ,
        
            "authority": "ROLE_USER"
        
    ],
    "authenticated": true,
    "principal": 
        "password": null,
        "username": "admin",
        "authorities": [
            
                "authority": "ROLE_ADMIN"
            ,
            
                "authority": "ROLE_USER"
            
        ],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
    ,
    "credentials": null,
    "name": "admin"

从第二个电话开始:

Request method: DELETE
Request path:   http://localhost:64588/api/books/04ad6d12-9b59-4ade-9a8a-e45daccb1f61
Proxy:          <none>
Request params: <none>
Query params:   <none>
Form params:    <none>
Path params:    id=04ad6d12-9b59-4ade-9a8a-e45daccb1f61
Multiparts:     <none>
Headers:        X-XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91
                Accept=*/*
Cookies:        JSESSIONID=7020E58A8D6DC2C883FD5D6BD086512A
                XSRF-TOKEN=6c44ae09-73f9-4115-bbbf-b01773ec1b91
Body:           <none>

HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Fri, 08 Jan 2016 07:44:21 GMT


    "timestamp": "2016-01-08T07:44:21.963+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Expected CSRF token not found. Has your session expired?",
    "path": "/api/books/04ad6d12-9b59-4ade-9a8a-e45daccb1f61"

【问题讨论】:

您可以根据需要禁用 CSRF 我知道,但我不想禁用它。 那么,我的回答对你有用吗? 【参考方案1】:

您需要在发布前执行 2 GET 才能在您的休息客户端或集成测试中使用 spring security CSRF 保护。

    发出GET 请求登录。这将返回 JSESSIONID 令牌和 XSRF-TOKEN 令牌。如果你使用返回的XSRF-TOKENPOST 会失败,因为我们使用空/假JSESSIONID 得到它。 使用上一个请求中的JSESSIONID,从第二个GET 中获取有用的XSRF-TOKEN。 现在您可以将XSRF-TOKEN 用作您的POST

使用基本身份验证和CSRF 保护放心的示例集成测试:

//1) get sessionId
Response response =
        given().auth().preemptive().basic(userName, userPassword).contentType(JSON).
        when().get(PREFIX_URL + "/user").
        then().log().all().extract().response();
String jsessionidId =  response.getSessionId();//or response.cookie("JSESSIONID");

//2) get XSRF-TOKEN using new/real sessionId
response =
        given().
        sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
        contentType(JSON).
        when().get(PREFIX_URL + "/user").
        then().log().all().extract().response();

//3) post data using XSRF-TOKEN
given().log().all().
        sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
        header("X-XSRF-TOKEN", response.cookie("XSRF-TOKEN")).
        queryParam("param",paramValue)).
        body(someData).
        contentType(JSON).
when().
        post(PREFIX_URL + "/post_some_data").
then().
    log().all().assertThat().statusCode(200);

【讨论】:

以上是关于使用 CSRF 进行放心和基本身份验证的集成测试的主要内容,如果未能解决你的问题,请参考以下文章

使用 X-XSRF-TOKEN(如 angularjs)标头确保 CSRF 保护

在集成测试中使用 oauth/openid 进行身份验证

OpenID-Connect 身份验证服务器的登录页面中是不是需要 CSRF 令牌?

使用 Microsoft.Owin.Testing.TestServer 进行内存集成测试的身份验证

VueJs 前端的无效 CSRF 令牌错误(symfony 5)

登录、用户身份验证、会话和 csrf 令牌 [关闭]