Python 使用令牌身份验证和functools.wrapps构建装饰器

Python 使用令牌身份验证和functools.wrapps构建装饰器,python,authentication,kivy,token,decorator,Python,Authentication,Kivy,Token,Decorator,我正在构建一个处理API请求的kivy应用程序。 get_token()函数在应用程序启动时首先运行,以获取所有必要的信息、dicts、vars等。令牌过期30分钟后,应用程序有一个按钮,按下它将向服务器发送请求调用,如果令牌过期,它将无法工作。我正在尝试构建一个decorator@use_token来检查是否执行了简单请求并收到了200个成功代码的响应,如果成功:return(修饰函数)else:调用get_token来刷新token,并返回修饰函数。一个完整的概念可以工作,在每个函数中的每个

我正在构建一个处理API请求的kivy应用程序。
get_token()
函数在应用程序启动时首先运行,以获取所有必要的信息、dicts、vars等。令牌过期30分钟后,应用程序有一个按钮,按下它将向服务器发送请求调用,如果令牌过期,它将无法工作。我正在尝试构建一个decorator
@use_token
来检查是否执行了简单请求并收到了200个成功代码的响应,如果成功:return(修饰函数)else:调用
get_token
来刷新token,并返回修饰函数。一个完整的概念可以工作,在每个函数中的每个可能的代码块上打印工作,但是当30分钟令牌过期后,
refresh\u-token()
调用if语句中的
get\u-token()
,打印仍在工作,但返回的修饰函数不发送请求,它打印测试字符串,但不执行主要任务。这告诉我函数
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)
装饰功能:

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
更改时,您不会更新
标题
变量

标题={
“内容类型”:“应用程序/json”,
“api_键”:readyToken
}
在python中,字符串是按值传递的。因此,在这里,您只需将
HEADERS['api_key']
设置为
readyToken
的当前值一次。如果以后更改
readyToken
,则不会更新
标题
,因为它只保留
readyToken
的原始值

只要在每次更改时更新
标题中的
readyToken
,即可解决此问题:

def get_令牌():
url_标记=”http://server.com"
有效载荷={
“grantType”:“密码”,
“密码”:“字符串”,
“刷新令牌”:“字符串”,
“令牌”:“字符串”,
“用户名”:“管理员”
}
标题={
“内容类型”:“应用程序/json”,
“api_密钥”:”
}
response=requests.post(url\u令牌,headers=headers,data=payload)
打印(f'Got Response:{Response.json()}')
全球准备就绪
readyToken=response.json()['token']
#我们还需要更新标题!
全局标题
标题['api_key']=readyToken
更好的是,如果您只在
标题中使用readyToken
,请完全删除readyToken变量,只需更新全局
标题
变量:

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

阿尔法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)

非常感谢您的建议1,付诸实施!我编辑了最后一段代码并添加了全局变量定义,我应该从一开始就在这里添加它,这有点重要)。你的第二点是完全正确的,变量
readyToken
在触发elif==401块时不会被更新,它将调用get_令牌函数,据我所知,它应该更新变量
readyToken
,但事实并非如此,我很确定我们接近解决方案)@jarrett.rus26更新了我的答案,希望能有帮助。很好!我很高兴听到你的问题得到了解决:)