aiohttp会话的NTLM身份验证

2024-04-26 12:22:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试将一个具有大量API调用(即获取密钥列表的数据)的应用程序迁移到使用asyncio,因为这是一项IO密集型任务。此API需要NTLM身份验证,因为它使用Active Directory凭据,为此我使用了以下代码:

session.auth = requests_ntlm.HttpNtlmAuth(username, password, session)

显然,asyncio使用aiohttp进行异步会话处理。因此,它是同步的,工作正常,但试图将其移动到更理想的异步/等待流,aiohttp只接受基本的身份验证凭据,如果NTLM auth被传递到^{,则会抛出错误TypeError: BasicAuth() tuple is required instead。以下是供参考的代码示例:

import asyncio
from aiohttp import ClientSession
from requests_ntlm import HttpNtlmAuth

async def fetch(url, session):
    async with session.get(url) as response:
        print(f"url: {url} ({response.status})")
        return await response.read()

async def run():
    url = "http://server/page/{}"
    tasks = []

    conn = aiohttp.TCPConnector(limit=10)
    async with ClientSession(connector=conn) as session:
        session.auth = HttpNtlmAuth(username, password, session)    # <--- Passing NTLM auth
        for page in range(100):
            task = asyncio.ensure_future(fetch(url.format(page), session))
            tasks.append(task)

        responses = await asyncio.gather(*tasks)

loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run())
loop.run_until_complete(future)

有没有办法将NTLM凭据传递给aiohttp会话并使其正常工作


Tags: runimportauthloopasynciourlasyncaiohttp
1条回答
网友
1楼 · 发布于 2024-04-26 12:22:46

好吧,有两种方法可以做到这一点


1路不是很好,因为它使用异步任务与loop.run_in_executor()
def make_request(url, username, password):
    session = requests.Session()
    session.verify = False
    session.auth = HttpNtlmAuth(username, password)
    response = session.get(url)
    if response.status_code != 401:
        print("SUCCESS! You can login with: %s : %s" % (username, password))
        quit()
    else:
        print(username, password)


async def create_and_proceed(url_obj, password_data, username_data):
    tasks = []
    amount = 0
    requests_amount = 0
    loop = asyncio.get_event_loop()
    for user in username_data:
        for password in password_data:
            if amount == 50:
                await asyncio.gather(*tasks)
                amount = 0
                tasks = []
            tasks.append(loop.run_in_executor(None, make_request, url_obj, user, password))
            amount += 1
            requests_amount += 1
            print(f"Amount: {str(requests_amount)}", flush=True, end="\r")

2路更好,但我真的不知道它是否可行。

如果您能够查看HttpNtlmAuth的源文件,您可以看到HttpNtlmAuth类是从requests.auth.AuthBase()继承的

class HttpNtlmAuth(AuthBase):
    """
    HTTP NTLM Authentication Handler for Requests.

    Supports pass-the-hash.
    """

    def __init__(self, username, password, session=None, send_cbt=True):
        """Create an authentication handler for NTLM over HTTP.

        :param str username: Username in 'domain\\username' format
        :param str password: Password
        :param str session: Unused. Kept for backwards-compatibility.
        :param bool send_cbt: Will send the channel bindings over a HTTPS channel (Default: True)
        """
        if ntlm is None:
            raise Exception("NTLM libraries unavailable")

        # parse the username
        try:
            self.domain, self.username = username.split('\\', 1)
        except ValueError:
            self.username = username
            self.domain = ''

        if self.domain:
            self.domain = self.domain.upper()
        self.password = password
        self.send_cbt = send_cbt

        # This exposes the encrypt/decrypt methods used to encrypt and decrypt messages
        # sent after ntlm authentication. These methods are utilised by libraries that
        # call requests_ntlm to encrypt and decrypt the messages sent after authentication
        self.session_security = None

让我们看看AuthBase()到底是关于什么的:

class AuthBase(object):
    """Base class that all auth implementations derive from"""

    def __call__(self, r):
        raise NotImplementedError('Auth hooks must be callable.')

因此,如果我是对的,那么AuthBase()类唯一要做的事情就是检查Auth钩子是否可调用。所以基本上你需要自己去实现它

相关问题 更多 >