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

时间:2020-10-13 19:44:43

标签: python authentication kivy token decorator

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

第一功能,在开始时运行ONES:

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)

2 个答案:

答案 0 :(得分:1)

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

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

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

这可以通过每次更改readyToken中的HEADERS来解决:

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']

答案 1 :(得分:0)

阿尔法Q非常感谢您,所有的猜测和修正都是正确的!我只在标头中使用令牌,因此我读取了readyToken并将令牌表达式正确放入'api_key'值中。我还重组了代码,并制作了一个实现所有修复程序的类:

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)