简体   繁体   English

如何处理python请求中的401(未授权)

[英]How to deal with 401 (unauthorised) in python requests

What I want to do is GET from a site and if that request returns a 401, then redo my authentication wiggle (which may be out of date) and try again.我想做的是从站点获取,如果该请求返回 401,则重做我的身份验证摆动(可能已过时)并重试。 But I don't want to try a third time, since that would be my authentication wiggle having the wrong credentials.但我不想第三次尝试,因为那将是我的身份验证摆动有错误的凭据。 Does anyone have a nice way of doing this that doesn't involve properly ugly code, ideally in python requests library, but I don't mind changing.有没有人有一个很好的方法来做到这一点,它不涉及非常丑陋的代码,最好是在 python 请求库中,但我不介意改变。

It doesn't get any less ugly than this, I think:没有比这更丑陋的了,我认为:

import requests
from requests.auth import HTTPBasicAuth

response = requests.get('http://your_url')

if response.status_code == 401:    
    response = requests.get('http://your_url', auth=HTTPBasicAuth('user', 'pass'))

if response.status_code != 200:
    # Definitely something's wrong

You could have wrapped this in a function and used a decorator to evaluate the response and retry the auth on 401. Then you only need to decorate any function that requires this re-auth logic....您可以将其包装在一个函数中并使用装饰器来评估响应并在 401 上重试身份验证。然后您只需要装饰任何需要此重新身份验证逻辑的函数....

Update: As requested, a code example.更新:根据要求,代码示例。 I'm afraid this one is an old piece of code, Python 2 based, but you'll get the idea.恐怕这是一段基于 Python 2 的旧代码,但您会明白的。 This one will retry an http call a number of times as defined in settings.NUM_PLATFORM_RETRIES and will call a refresh_token on auth failures.这将重试一次 http 调用,如settings.NUM_PLATFORM_RETRIES定义的那样,并将在身份验证失败时调用refresh_token you can adjust the use case and result to whatever.您可以将用例和结果调整为任何内容。 You can then use this decorator around methods:然后,您可以在方法周围使用此装饰器:

@retry_on_read_error
def some_func():
   do_something()



def retry_on_read_error(fn):
    """
    Retry Feed reads on failures
    If a token refresh is required it is performed before retry.
    This decorator relies on the model to have a refresh_token method defined, othewise it will fail
    """
    @wraps(fn)
    def _wrapper(self, *args, **kwargs):
        for i in range(settings.NUM_PLATFORM_RETRIES):
            try:
                res = fn(self, *args, **kwargs)

                try:
                    _res = json.loads(res)
                except ValueError:
                    # not a json response (could be local file read or non json data)
                    return res

                if 'error' in _res and _res['error']['status'] in (401, 400):
                    raise AccessRefusedException(_res['error']['message'])

                return res
            except (urllib2.URLError, IOError, AccessRefusedException) as e:
                if isinstance(e, AccessRefusedException):
                    self.refresh_token()
                continue
        raise ApiRequestFailed(
            "Api failing, after %s retries: %s" % (settings.NUM_PLATFORM_RETRIES, e), args, kwargs
        )

    return _wrapper

You can use something like this你可以使用这样的东西

# 401 retry strategy

import requests
from requests import Request, Session, RequestException


    class PreparedRequest:
    """
    Class to make Http request with 401 retry
    """
        failedRequests = []
        defaultBaseUrl = "https://jsonplaceholder.typicode.com"
        MAX_RETRY_COUNT = 0

        def __init__(self, method, endpoint,
             baseurl=defaultBaseUrl, headers=None, data=None, params=None):
        """
        Constructor for PreparedRequest class
        @param method: Http Request Method
        @param endpoint: endpoint of the request
        @param headers: headers of the request
        @param data: data of request
        @param params: params of the request
        """
        self.method = method
        self.url = baseurl + endpoint
        self.headers = headers
        self.data = data
        self.params = params
        self.response = None

    def send(self):
    """
    To send http request to the server
    @return: response of the request
    """
        req = Request(method=self.method, url=self.url, data=self.data, 
                headers=self.headers,params=self.params)
        session = Session()
        prepared = session.prepare_request(req)
        response = session.send(prepared)
        if response.status_code == 200:
            PreparedRequest.failedRequests.append(self)
            PreparedRequest.refresh_token()
        elif response.status_code == 502:
            raise Exception(response.raise_for_status())
        else:
            self.response = session.send(prepared)

    @staticmethod
    def refresh_token():
        if PreparedRequest.MAX_RETRY_COUNT > 3:
            return
        print("Refreshing the token")
        # Write your refresh token strategy here
        PreparedRequest.MAX_RETRY_COUNT += 1
        total_failed = len(PreparedRequest.failedRequests)
        for i in range(total_failed):
            item = PreparedRequest.failedRequests.pop()
            item.send()


r = PreparedRequest(method="GET", endpoint="/todos/")
r.send()
print(r.response.json())

You need to send in the header of the request the authentication param您需要在请求的 header 中发送身份验证参数

import requests
from requests.auth import HTTPBasicAuth

auth = HTTPBasicAuth("username", "password")
response = requests.get("http://serverIpOrName/html", auth=auth)
if response.status_code == 401 : 
    print("Authentication required")
if response.status_code == 200:
    print(response.content)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM