具有令牌身份验证和 functools.wraps 的建筑装饰器

building decorator with token authentication and functools.wraps

我正在构建一个执行 API 请求的 kivy 应用程序。 get_token() 函数在启动应用程序时首先运行,以获取所有必要的信息、字典、变量等。30 分钟令牌过期后,应用程序有一个按钮,on_press 它将向服务器发送请求调用,如果令牌已过期,它将无法工作。我正在尝试构建一个装饰器 @use_token,它将检查简单请求是否被执行并接收响应 200 成功代码,如果成功:return(装饰函数)否则:调用 get_token 将刷新令牌和 returning 修饰函数。整个概念有效,打印在每个函数的每个可能的代码块上工作,但是当 30 分钟令牌过期后,refresh_token() 在 if 语句中调用 get_token(),打印仍然有效,但 returned 装饰函数不发送请求,它打印测试字符串但不做主要的事情。这告诉我函数 get_token() 在 if 语句中执行但不更新令牌信息...

第一个函数,在开始时运行:

def get_token():
    url_token = "http://server.com"
    payload = "{" \
              "\n  \"grantType\": \"password\"," \
              "\n  \"password\": \"string\"," \
              "\n  \"refreshToken\": \"string\"," \
              "\n  \"token\": \"string\"," \
              "\n  \"username\": \"admin\"" \
              "\n}"
    headers = {
            'Content-Type': 'application/json',
            'api_key': ''
        }
    global readyToken
    readyToken = requests.request("POST", url_token, headers=headers, data=payload).json()['token']
    print("Getting a NEW TOKEN!")
get_token()

装饰函数:

def use_token(func):
    @functools.wraps(func)
    def refresh_token(*args):
        url_check = "simplerequest.com"
        response = requests.request("PUT", url_check, headers=HEADERS)
        print("This print from url_check block "+str(response))
        str_response = str(response)
        if '401' in str_response:
            print("401 found, Token is Expired, refreshing with get_token")
            get_token()
        else:
            print("200 Code, success, passing, leaving else statement")
            pass
        print("emptying str_response and calling for decorated function:")
        str_response = ""
        return func(*args)
    return refresh_token

修饰函数:

global URL_QC, HEADERS

URL_QC = "www.server.com"
HEADERS = {
        'Content-Type': 'application/json',
        'api_key': readyToken
}


@use_token
def change_channel(self, display_mac, ch_number):
    print("Hello from DECORATED function!!!")
    payload = "{\"deviceIds\": [" + str(display_mac) + "],\"menu\": \"save_sch_channel\", \"productType\": \"string\", \"value\":" + str(ch_number) + "}"
    response = requests.request("PUT", URL_QC, headers=HEADERS, data=payload)

您没有在 readyToken 更改时更新 HEADERS 变量。

HEADERS = {
  'Content-Type': 'application/json',
  'api_key': readyToken
}

在python中,字符串是按值传递的。所以在这里,您只将 HEADERS['api_key'] 设置为 readyToken 的当前值一次。如果稍后更改 readyToken,则 HEADERS 不会更新,因为它只保留 readyToken.

的原始值

这可以通过在 HEADERS 每次更改时更新 readyToken 来解决:

def get_token():
    url_token = "http://server.com"
    payload = {
        "grantType": "password",
        "password":  "string",
        "refreshToken": "string",
        "token": "string",
        "username": "admin"
    }
    headers = {
        "Content-Type": 'application/json',
        "api_key": ""
    }
    response = requests.post(url_token, headers=headers, data=payload)
    print(f'Got Response: {response.json()}')
    global readyToken
    readyToken = response.json()['token']

    # We also need to update headers!
    global HEADERS
    HEADERS['api_key'] = readyToken

更好的是,如果您只在 HEADERS 中使用 readyToken,请完全删除 readyToken 变量并简单地更新全局 HEADERS 变量:

def get_token():
    ...
    response = requests.post(url_token, headers=headers, data=payload)
    print(f'Got Response: {response.json()}')
    global HEADERS
    HEADERS['api_key'] = response.json()['token']

Alfa Q 非常感谢你,你所有的猜测和修正都是正确的!我只在 header 中使用令牌,因此我阅读了 readyToken 并将令牌表达式直接放入 'api_key' 值中。我还重组了代码并制作了一个 class 实现所有修复:

token.py:

class Token:
    def __init__(self):
        self.url_check = "www.check.com"
        self.url_token = "www.server.com"
        self.payload = "{" \
                  "\n  \"grantType\": \"password\"," \
                  "\n  \"password\": \"password\"," \
                  "\n  \"refreshToken\": \"string\"," \
                  "\n  \"token\": \"string\"," \
                  "\n  \"username\": \"admin\"" \
                  "\n}"
        self.headers_token = {
                'Content-Type': 'application/json',
                'api_key': self.get_token()
            }

    def get_token(self):
        return requests.post(self.url_token, headers=self.headers, data=self.payload).json()['token']

    def use_token(self, func):
        @functools.wraps(func)
        def refresh_token(*args):
            response = requests.put(self.url_check, headers=self.headers_token)
            print("This print from url_check block: " + str(response))
            if response.status_code == 200:
                print('Status 200 | Token OK - No refresh necessary')
                return func(*args)
            elif response.status_code == 401:
                print('Status 401 | Token is Expired - Refreshing')
                self.get_token()
                return func(*args)
            else:
                print(f'Status {response.status_code} | Error Occurred')
                print("Print from REFRESH_TOKEN")
        return refresh_token

main.py:

@token.use_token
def change_channel(self, display_mac, ch_number):
    print("Hello from DECORATED function!!!")
    payload = "{\"deviceIds\": [" + str(display_mac) + "],\"menu\": \"save_sch_channel\", \"productType\": \"string\", \"value\":" + str(ch_number) + "}"
    response = requests.request("PUT", URL_QC, headers=token.headers_token, data=payload)
    print(response)