使用 Cypress.io 以编程方式登录 Django 服务器(不使用 UI)

JL *_*ret 8 django cypress

肯定遗漏了一些明显的东西,但由于它的 CSRF 保护,我非常坚持登录到 Django。

查看了使用 cy.getCookie() 来测试使用 HTML Web 表单登录的示例食谱,但如果它推荐的第一件事是禁用 CSRF,那确实没有多大帮助。

Django 想要什么:

这是正常的、受 CSRF 保护的 Django 登录视图在其传入的 POST 数据中所期望的:

csrfmiddlewaretoken=Y5WscShtwZn3e1eCyahdqPURbfHczLyXfyPRsEOWacdUcGNYUn2EK6pWyicTLSXT
username=guest
password=password
next
Run Code Online (Sandbox Code Playgroud)

它不是在请求头中寻找 CSRF,也不x-csrf-token是在响应头上设置。

在此处输入图片说明

而且,使用我的代码,我从不传入让 Django 返回 403 错误的 csrf 令牌。

Cypress.Commands.add("login", (username, password) => {
    var login_url = Cypress.env("login_url");

    cy.visit(login_url)

    var hidden_token = cy.get("input[name='csrfmiddlewaretoken']").value;
    console.log(`hidden_token:${hidden_token}:`)

    console.log(`visited:${login_url}`)
    var cookie = cy.getCookie('csrftoken');
    // debugger;

    var csrftoken = cy.getCookie('csrftoken').value;
    console.log(`csrftoken:${csrftoken}:`) 
    console.log(`request.POST`)

    cy.request({
        method: 'POST',
        form: true,
        url: login_url,
        // body: {'username': 'guest', 'password': 'password', 'csrfmiddlewaretoken': cy.getCookie('csrftoken').value}
        body: {'username': 'guest', 'password': 'password', 'csrfmiddlewaretoken': hidden_token}
    })
})
Run Code Online (Sandbox Code Playgroud)

赛普拉斯的错误:

在此处输入图片说明

我怀疑 POST 数据与undefined通过隐藏表单输入或 cookie 获取方法获得的令牌有关,如console.logfor所示。

现在,我已经开始查看https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/logging-in__csrf-tokens,我认为我应该能够调整策略 #1 :从 HTML 解析令牌以进行提取,$("input[name='csrfmiddlewaretoken']").value但我希望之前有人这样做过。

我的另一个想法是有条件地向 Django 添加一个请求中间件,该中间件将从请求标头中获取 csrftoken 并在丢失时注入 POST 表单数据。如果我在CSRF内容之前插入它以触发,那会起作用吗?

最后,我计划将sessionid令牌排除在重置之外,以便我可以在登录一次后运行多个测试。

env:Django 1.10,cypress 1.4.2,现在升级到 2.0.0,同样的问题。

phk*_*phk 9

您可以使用HEAD请求获取登录所需的第一个 CSRF 令牌并查看 cookie(无需解析页面)。

此外,您可以让您的自定义cy.login()返回令牌(异步,因此您需要使用.then()),而不必cy.getCookie('csrftoken')再次调用,如果您之后需要为 POST 请求提供令牌等:

Cypress.Commands.add('login', (username, password) => {

  return cy.request({
    url: '/login/',
    method: 'HEAD' // cookies are in the HTTP headers, so HEAD suffices
  }).then(() => {

    cy.getCookie('sessionid').should('not.exist')
    cy.getCookie('csrftoken').its('value').then((token) => {
      let oldToken = token
      cy.request({
        url: '/login/',
        method: 'POST',
        form: true,
        followRedirect: false, // no need to retrieve the page after login
        body: {
          username: username,
          password: password,
          csrfmiddlewaretoken: token
        }
      }).then(() => {

        cy.getCookie('sessionid').should('exist')
        return cy.getCookie('csrftoken').its('value')

      })
    })
  })

})
Run Code Online (Sandbox Code Playgroud)

注意:登录后令牌会发生变化,因此cy.getCookie('csrftoken')需要调用两次。

之后,您可以在测试中按以下方式使用它(请参阅https://docs.djangoproject.com/en/dev/ref/csrf/了解为什么需要标题):

cy.login().then((csrfToken) => {

  cy.request({
    method: 'POST',
    url: '/api/baz/',
    body: { 'foo': 'bar' },
    headers: { 'X-CSRFToken': csrfToken }
  })

})
Run Code Online (Sandbox Code Playgroud)

  • 我发现“HEAD”不起作用,但是用“GET”替换它就可以了,还需要“let oldToken = token”,但非常有用,谢谢 (2认同)

bku*_*era 0

你是对的,赛普拉斯没有在正文中发送令牌,因为它是,因为你在 上undefined使用的方式来获取令牌。.get()input

您正在使用.get()同步调用,但它实际上是async。这是因为 Cypress 会智能地重试查找 DOM 元素,而这需要不确定的时间。这是赛普拉斯支持内置测试的核心概念。Cypress 文档比我更详细地介绍了这一点,因此请在此处查看: https: //docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Default-Assertions

在您的情况下,如何访问 DOM 中元素的属性应该放在回调中:

cy.get("input[name='csrfmiddlewaretoken']").then($input=>{
    const hidden_token = $input.val()
    cy.request({
        method: 'POST',
        form: true,
        url: login_url,
        // body: {'username': 'guest', 'password': 'password', 'csrfmiddlewaretoken': cy.getCookie('csrftoken').value}
        body: {'username': 'guest', 'password': 'password', 'csrfmiddlewaretoken': hidden_token}
    })
})
Run Code Online (Sandbox Code Playgroud)

...

专业提示:使用赛普拉斯的文档搜索通常可以为您提供所需的信息 在此输入图像描述