Python请求ajax表单身份验证问题

Posted

技术标签:

【中文标题】Python请求ajax表单身份验证问题【英文标题】:Python requests ajax form authentication issue 【发布时间】:2022-01-19 08:55:13 【问题描述】:

我似乎是一个明显的问题,以编程方式登录网站,然后在通过身份验证时获取数据。

我已经阅读 *** 几天了,但找不到可行的解决方案。

这里是登录表单,也可以使用单独的 URL 访问,通过浏览器登录后它会重定向到主页:

<strong class="popup-title">i class="fa fa-lock" aria-hidden="true"></i>Login</strong>
<div class="popup-holder">
    <form action="https://test.com/login/" data-form="ajax" method="post">
        <div class="generic-error hidden">
        </div>
        <div>
            <div class="row">
                <label for="login_username" class="field-label required">Username</label>
                <input type="text" name="username" id="login_username" class="textfield"
                    placeholder="Enter your username" />
                <div class="field-error down"></div>
            </div>

            <div class="row">
                <label for="login_pass" class="field-label required">Password</label>
                <input type="password" name="pass" id="login_pass" class="textfield" placeholder="Enter your password" />
                <div class="field-error down"></div>
            </div>

            <div class="row">
                <div class="col-sm-4" style="padding-left: 0;">
                    <input type="checkbox" name="remember_me" id="login_remember_me" class="checkbox" value="1" checked />
                    <label for="login_remember_me">remember me</label>
                </div>
                <div class="col-sm-5 forgot pull-right" style="padding-right: 0px;">
                    <a href="https://test.com/reset-password/" data-fancybox="ajax">Forgot password?</a><br />
                    <a href="https://test.com/resend-confirmation/" data-fancybox="ajax">Missing confirmation email?</a>
                </div>
            </div>

            <div class="row">
                <input type="hidden" name="action" value="login" />
                <input type="hidden" name="email_link" value="https://test.com/email/" />
                <input type="submit" class="btn btn-danger btn-lg btn-block" value="Log in" />
            </div>
            <div class="row">
                <span class="form-separator">Not a member yet? Sign up now for free!</span>
            </div>
            <div class="row">
                <a href="https://test.com/signup/" class="btn btn-info btn-lg btn-block" data-fancybox="ajax">Sign up</a>
            </div>
        </div>
    </form>
</div>

这是我尝试过的 Python 代码:

payload = 
   'username': 'mylogin',
   'pass': 'mypass'


with requests.Session() as s:
    r = s.post('https://test.com/login/', data=payload)
    r = s.get('https://test.com/testpage/')

PowerShell 中的逻辑相同:

$payload = @
   username = 'mylogin'
   pass = 'mypass'


$r = Invoke-RestMethod 'https://test.com/login/' -Method POST -Body $payload -SessionVariable 'Session'
$r = Invoke-WebRequest -Uri "https://test.com/testpage/" -WebSession $Session

但上述方法均无效,我仍在为未经身份验证的用户获取结果。

【问题讨论】:

Web服务器是否使用了登录时需要返回的会话cookie? 是的,从我在 Postman 中看到的情况来看,问题获取登录表单的请求后有两个 cookie:phpSESSID=48947532f789fd83711946b87d07a309;路径=/;域=test.com;安全的; kt_ips=178.137.7.23;路径=/;域=test.com;安全的;过期=格林威治标准时间 2021 年 12 月 17 日星期五 11:59:10; 那么,您需要在 POST 请求中返回这些 cookie:cookies = 'PHPSESSID': '48947532f789fd83711946b87d07a309' r = requests.post(''https://test.com/login/, cookies=cookies, data=payload)。但是您需要先发出 GET 请求才能获取实际的 cookie 值。 尝试了不同的方式,使用 Session 和没有它,在我看来,使用它更方便,因为理论上应该自动设置 cookie 和 headers,所以我不必手动设置它们。但无论如何,只是尝试获取页面,提取 cookie 值,然后使用 post 和后续 get 方法发回,但仍然为未经身份验证的用户获取结果。 【参考方案1】:

这是一个使用我的一个 Django 站点和一个演示登录帐户的工作示例。

requests.Session() 用于管理 cookie。为了让它工作,我必须在发布登录之前明确管理标题内容,例如添加Referer

import requests
import re

base_url = 'https://www.archery-analytics.com/en/'

# use session object to manage cookies and headers
s = requests.Session()
s.headers.update(
    'Host': 'www.archery-analytics.com',
    'Origin': 'https://www.archery-analytics.com',
    )

# get login form and cookies
r1 = s.get(base_url + 'public/home')
print(r1.status_code, r1.url)

# add Referer to header
s.headers.update(
    'Referer': r1.url,
    )

# get csrf token of form (= hidden input element of login form)
reggie = re.compile(rb".*name=\"csrfmiddlewaretoken\" value=\"(?P<csrf>\w+)\".*")
match = reggie.findall(r1.content)

# login data for demo account
payload = 
    'username': 'RyngDyng',
    'password': '123demo123',
    'login': '',
    'csrfmiddlewaretoken': match[0].decode("utf-8")


# login post
r2 = s.post(base_url + 'global/login', data=payload)
print(r2.status_code, r2.url)

# check successful login
if r2.status_code == requests.codes.ok:

    # test logged in: access to page for editing user profile
    r3 = s.get(base_url + 'global/edit_profile')
    print(r3.status_code, r3.url)
    
    
    # logout
    r4 = s.get(base_url + 'global/logout')
    print(r4.status_code, r4.url)

输出:

200 https://www.archery-analytics.com/en/public/home
200 https://www.archery-analytics.com/en/public/home
200 https://www.archery-analytics.com/en/global/edit_profile
200 https://www.archery-analytics.com/en/public/home

【讨论】:

刚试过 - 不工作( 我建议使用浏览器分析成功登录的 HTTP 标头,然后执行 r = requests.post(...) 并将标头 r.request.headers 与成功登录标头进行比较。然后,更新标头以使它们相等(就像我上面的引用者示例一样)。 如果你分享一个真实的 url 和演示登录凭据,我也可以检查一下 再挖一点,你的方法让我找到了一个解决方案,使用浏览器开发工具将 URL 复制为 curl 命令,然后我使用了一些从 curl 到 python 的在线转换器,最后,它成功了!

以上是关于Python请求ajax表单身份验证问题的主要内容,如果未能解决你的问题,请参考以下文章

Ajax 和表单身份验证

使用 jQuery/ajax 的基本身份验证

无需重定向的 Django 远程身份验证

MVC 身份验证超时/会话 cookie 删除后的 Ajax 请求

对需要身份验证的云运行服务的 Ajax 请求

Laravel API ajax 请求未通过身份验证