最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python接口自動(dòng)化中Token過(guò)期的9種解決方案

 更新時(shí)間:2026年01月15日 09:50:42   作者:愛(ài)吃 香菜  
在進(jìn)行Python接口自動(dòng)化測(cè)試時(shí),相信大家都遇到過(guò)這樣的困擾Token過(guò)期導(dǎo)致測(cè)試用例批量失敗,已經(jīng)成為接口自動(dòng)化測(cè)試中最常見(jiàn)的攔路虎,別擔(dān)心,今天就來(lái)徹底解決這個(gè)難題,需要的朋友可以參考下

為什么Token管理是接口自動(dòng)化的痛點(diǎn)?

在進(jìn)行Python接口自動(dòng)化測(cè)試時(shí),相信大家都遇到過(guò)這樣的困擾:

? 每次跑用例前手動(dòng)更新 Token

? 測(cè)試中途 Token 過(guò)期,用例批量失敗

? 多環(huán)境(dev/test/prod)Token 管理混亂

? 登錄接口變化,所有用例都要改

Token過(guò)期導(dǎo)致測(cè)試用例批量失敗,已經(jīng)成為接口自動(dòng)化測(cè)試中最常見(jiàn)的"攔路虎"。

別擔(dān)心,今天就來(lái)徹底解決這個(gè)難題。我們將會(huì)從“為什么Token需要自動(dòng)刷新”、“Token 刷新的常見(jiàn)場(chǎng)景”以及“Token過(guò)期的9種解決方案”三方面進(jìn)行詳述。

為什么Token需要自動(dòng)刷新?

在Python接口自動(dòng)化測(cè)試中,Token自動(dòng)刷新不是一個(gè)可有可無(wú)的"錦上添花"功能,而是確保測(cè)試穩(wěn)定性和可靠性的核心機(jī)制。

例如:大多數(shù)基于Token的認(rèn)證機(jī)制(如JWT、OAuth2)都會(huì)為T(mén)oken設(shè)置一個(gè)有效期(例如30分鐘、2小時(shí)等)。一旦Token過(guò)期,接口就會(huì)返回401 Unauthorized錯(cuò)誤,導(dǎo)致測(cè)試用例失敗。這會(huì)導(dǎo)致測(cè)試的連續(xù)性和穩(wěn)定性,影響測(cè)試效率,也會(huì)影響自動(dòng)化測(cè)試模擬用戶真實(shí)行為(如用戶會(huì)在Token過(guò)期后通過(guò)刷新Token或重新登錄來(lái)繼續(xù)操作)。

Token 刷新的常見(jiàn)場(chǎng)景

Token 刷新的常見(jiàn)場(chǎng)景,如下圖:

我們要做的,就是讓程序能自動(dòng)判斷并處理這四種情況。

Token過(guò)期的9種解決方案

解決方案1

解決方案1:簡(jiǎn)單粗暴型 - 每次請(qǐng)求前重新登錄

核心思路:

每次發(fā)送請(qǐng)求前都先調(diào)用登錄接口獲取新Token

代碼實(shí)現(xiàn):

def get_fresh_token():
    """每次請(qǐng)求前獲取新Token"""
    login_data = {"username": "testuser", "password": "testpass"}
    resp = requests.post(f"{BASE_URL}/login", json=login_data)
    return resp.json()["data"]["access_token"]
def test_api_with_fresh_token():
    token = get_fresh_token()
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.get(f"{BASE_URL}/api/data", headers=headers)
    return response.json()

優(yōu)缺點(diǎn)分析:

優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,保證每次都是新Token;

缺點(diǎn):效率極低,增加額外請(qǐng)求,頻繁登錄可能觸發(fā)風(fēng)控。

解決方案2

解決方案2:前置獲取型 - 用例執(zhí)行前統(tǒng)一獲取

核心思路:

在測(cè)試套件執(zhí)行前一次性獲取Token,所有用例共享。

代碼實(shí)現(xiàn):

import pytest
class TestBase:
    token = None
    @classmethod
    def setup_class(cls):
        """測(cè)試類執(zhí)行前獲取Token"""
        cls.token = cls.get_token_once()
    @classmethod
    def get_token_once(cls):
        login_data = {"username": "testuser", "password": "testpass"}
        resp = requests.post(f"{BASE_URL}/login", json=login_data)
        return resp.json()["data"]["access_token"]
    def test_user_info(self):
        headers = {"Authorization": f"Bearer {self.token}"}
        response = requests.get(f"{BASE_URL}/user/info", headers=headers)
        assert response.status_code == 200

優(yōu)缺點(diǎn)分析:

優(yōu)點(diǎn):減少登錄次數(shù),提高效率;

缺點(diǎn):長(zhǎng)時(shí)間運(yùn)行測(cè)試時(shí)Token仍會(huì)過(guò)期。

解決方案3

解決方案3:智能檢測(cè)型 - 請(qǐng)求失敗時(shí)重新登錄

核心思路:

捕獲401錯(cuò)誤,自動(dòng)重新登錄并重試請(qǐng)求

代碼實(shí)現(xiàn):

class SmartTokenClient:
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.username = username
        self.password = password
        self.token = None
    def login(self):
        """登錄獲取Token"""
        login_data = {
            "username": self.username,
            "password": self.password
        }
        resp = requests.post(f"{self.base_url}/login", json=login_data)
        self.token = resp.json()["data"]["access_token"]
        return self.token
    def request_with_retry(self, method, endpoint, **kwargs):
        """帶重試機(jī)制的請(qǐng)求"""
        if not self.token:
            self.login()
        headers = kwargs.get('headers', {})
        headers['Authorization'] = f'Bearer {self.token}'
        kwargs['headers'] = headers
        response = requests.request(method, f"{self.base_url}{endpoint}", **kwargs)
        # 檢測(cè)到Token過(guò)期,重新登錄并重試
        if response.status_code == 401:
            print("檢測(cè)到Token過(guò)期,重新登錄...")
            self.login()
            kwargs['headers']['Authorization'] = f'Bearer {self.token}'
            response = requests.request(method, f"{self.base_url}{endpoint}", **kwargs)
        return response
# 使用示例
client = SmartTokenClient(BASE_URL, "testuser", "testpass")
response = client.request_with_retry("GET", "/api/users")

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn):自動(dòng)處理過(guò)期情況,用戶體驗(yàn)好;

缺點(diǎn):第一次401錯(cuò)誤無(wú)法避免,需要重試。

解決方案4

解決方案4:主動(dòng)刷新型 - 定期刷新Token

核心思路:

記錄Token獲取時(shí)間,在過(guò)期前主動(dòng)刷新

代碼實(shí)現(xiàn):

import time
class ProactiveTokenClient:
    def __init__(self, base_url, username, password, token_ttl=1800):
        self.base_url = base_url
        self.username = username
        self.password = password
        self.token_ttl = token_ttl  # Token有效期,默認(rèn)30分鐘
        self.token = None
        self.token_time = 0
    def get_token(self):
        """獲取Token,如果快過(guò)期則主動(dòng)刷新"""
        current_time = time.time()
        # 沒(méi)有Token或Token即將過(guò)期(提前5分鐘刷新)
        if not self.token or (current_time - self.token_time) > (self.token_ttl - 300):
            self.refresh_token()
        return self.token
    def refresh_token(self):
        """刷新Token"""
        login_data = {
            "username": self.username, 
            "password": self.password
        }
        resp = requests.post(f"{self.base_url}/login", json=login_data)
        self.token = resp.json()["data"]["access_token"]
        self.token_time = time.time()
        print(f"Token已刷新: {self.token[:20]}...")
    def get(self, endpoint, **kwargs):
        """GET請(qǐng)求"""
        token = self.get_token()
        headers = kwargs.get('headers', {})
        headers['Authorization'] = f'Bearer {token}'
        kwargs['headers'] = headers
        return requests.get(f"{self.base_url}{endpoint}", **kwargs)

優(yōu)缺點(diǎn)分析:

優(yōu)點(diǎn):避免401錯(cuò)誤,用戶體驗(yàn)最佳;

缺點(diǎn):需要知道Token準(zhǔn)確有效期,實(shí)現(xiàn)稍復(fù)雜。

解決方案5

解決方案5:雙Token輪換型 - Access Token + Refresh Token

核心思路:

使用Refresh Token靜默刷新Access Token,用戶無(wú)感知

代碼實(shí)現(xiàn):

class DualTokenClient:
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.username = username
        self.password = password
        self.access_token = None
        self.refresh_token = None
        self.setup_tokens()
    def setup_tokens(self):
        """初始獲取雙Token"""
        login_data = {
            "username": self.username,
            "password": self.password
        }
        resp = requests.post(f"{self.base_url}/login", json=login_data)
        token_data = resp.json()["data"]
        self.access_token = token_data["access_token"]
        self.refresh_token = token_data["refresh_token"]
    def refresh_access_token(self):
        """使用Refresh Token刷新Access Token"""
        refresh_data = {
            "refresh_token": self.refresh_token
        }
        try:
            resp = requests.post(f"{self.base_url}/refresh", json=refresh_data)
            if resp.status_code == 200:
                token_data = resp.json()["data"]
                self.access_token = token_data["access_token"]
                # 有些系統(tǒng)會(huì)返回新的refresh_token
                if "refresh_token" in token_data:
                    self.refresh_token = token_data["refresh_token"]
                return True
        except Exception as e:
            print(f"刷新Token失敗: {e}")
        # 刷新失敗,重新登錄
        self.setup_tokens()
        return False
    def request(self, method, endpoint, max_retry=1, **kwargs):
        """支持Token自動(dòng)刷新的請(qǐng)求"""
        for attempt in range(max_retry + 1):
            headers = kwargs.get('headers', {})
            headers['Authorization'] = f'Bearer {self.access_token}'
            kwargs['headers'] = headers
            response = requests.request(method, f"{self.base_url}{endpoint}", **kwargs)
            if response.status_code != 401:
                return response
            if attempt < max_retry:
                print("Access Token過(guò)期,嘗試刷新...")
                if self.refresh_access_token():
                    continue
        return response

優(yōu)缺點(diǎn)分析:

優(yōu)點(diǎn):用戶體驗(yàn)極佳,安全性高;

缺點(diǎn):需要服務(wù)端支持雙Token機(jī)制。

解決方案6

解決方案6:裝飾器模式 - 無(wú)侵入式Token管理

核心思路:

使用裝飾器自動(dòng)為請(qǐng)求方法添加Token管理功能。

代碼實(shí)現(xiàn)???????

def auto_token_refresh(func):
    """自動(dòng)Token刷新裝飾器"""
    def wrapper(self, *args, **kwargs):
        # 確保有有效Token
        self.ensure_valid_token()
        # 執(zhí)行原函數(shù)
        result = func(self, *args, **kwargs)
        # 如果返回401,刷新Token后重試
        if hasattr(result, 'status_code') and result.status_code == 401:
            print("檢測(cè)到401錯(cuò)誤,自動(dòng)刷新Token并重試...")
            self.refresh_token()
            result = func(self, *args, **kwargs)
        return result
    return wrapper
class DecoratorTokenClient:
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.username = username
        self.password = password
        self.token = None
    def login(self):
        """登錄獲取Token"""
        login_data = {
            "username": self.username,
            "password": self.password
        }
        resp = requests.post(f"{self.base_url}/login", json=login_data)
        self.token = resp.json()["data"]["access_token"]
        return self.token
    def refresh_token(self):
        """刷新Token"""
        return self.login()
    def ensure_valid_token(self):
        """確保有有效的Token"""
        if not self.token:
            self.login()
    @auto_token_refresh
    def get_users(self):
        """獲取用戶列表"""
        headers = {"Authorization": f"Bearer {self.token}"}
        return requests.get(f"{self.base_url}/users", headers=headers)
    @auto_token_refresh  
    def create_order(self, order_data):
        """創(chuàng)建訂單"""
        headers = {"Authorization": f"Bearer {self.token}"}
        return requests.post(f"{self.base_url}/orders", json=order_data, headers=headers)
# 使用示例
client = DecoratorTokenClient(BASE_URL, "testuser", "testpass")
response = client.get_users()  # 自動(dòng)處理Token

優(yōu)缺點(diǎn)分析:

優(yōu)點(diǎn):代碼簡(jiǎn)潔,無(wú)侵入性,易于維護(hù);

缺點(diǎn):需要為每個(gè)方法添加裝飾器。

解決方案7

解決方案7:會(huì)話保持型 - 使用requests.Session。

核心思路:

利用Session對(duì)象保持登錄狀態(tài),自動(dòng)處理Cookie。

代碼實(shí)現(xiàn):???????

class SessionTokenClient:
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.username = username
        self.password = password
        self.session = requests.Session()
        self.is_logged_in = False
    def login(self):
        """登錄并保持會(huì)話"""
        login_data = {
            "username": self.username,
            "password": self.password
        }
        response = self.session.post(f"{self.base_url}/login", json=login_data)
        if response.status_code == 200:
            self.is_logged_in = True
            # 如果是Token認(rèn)證,可能需要手動(dòng)設(shè)置Header
            token = response.json()["data"]["access_token"]
            self.session.headers.update({"Authorization": f"Bearer {token}"})
        return response
    def ensure_login(self):
        """確保處于登錄狀態(tài)"""
        if not self.is_logged_in:
            self.login()
    def get(self, endpoint, **kwargs):
        """GET請(qǐng)求"""
        self.ensure_login()
        response = self.session.get(f"{self.base_url}{endpoint}", **kwargs)
        # 檢查是否因會(huì)話過(guò)期而失敗
        if response.status_code == 401:
            print("會(huì)話過(guò)期,重新登錄...")
            self.login()
            response = self.session.get(f"{self.base_url}{endpoint}", **kwargs)
        return response
    def post(self, endpoint, **kwargs):
        """POST請(qǐng)求"""
        self.ensure_login()
        response = self.session.post(f"{self.base_url}{endpoint}", **kwargs)
        if response.status_code == 401:
            print("會(huì)話過(guò)期,重新登錄...")
            self.login()
            response = self.session.post(f"{self.base_url}{endpoint}", **kwargs)
        return response
# 使用示例
client = SessionTokenClient(BASE_URL, "testuser", "testpass")
response = client.get("/api/users")  # 自動(dòng)處理會(huì)話

優(yōu)缺點(diǎn)分析:

優(yōu)點(diǎn):自動(dòng)處理Cookie,適合基于會(huì)話的系統(tǒng);

缺點(diǎn):不適合純Token認(rèn)證的系統(tǒng)。

解決方案8

解決方案8:工廠模式 - 多環(huán)境Token管理

核心思路:

使用工廠模式管理不同環(huán)境的Token,支持動(dòng)態(tài)切換。

代碼實(shí)現(xiàn):???????

from abc import ABC, abstractmethod
class TokenManager(ABC):
    """Token管理器抽象類"""
    @abstractmethod
    def get_token(self) -> str:
        pass
    @abstractmethod
    def refresh_token(self) -> bool:
        pass
class DevTokenManager(TokenManager):
    """開(kāi)發(fā)環(huán)境Token管理器"""
    def get_token(self) -> str:
        # 開(kāi)發(fā)環(huán)境可能返回固定Token或mock Token
        return "dev_mock_token"
    def refresh_token(self) -> bool:
        return True
class TestTokenManager(TokenManager):
    """測(cè)試環(huán)境Token管理器"""
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.username = username
        self.password = password
        self.token = None
    def get_token(self) -> str:
        if not self.token:
            self.refresh_token()
        return self.token
    def refresh_token(self) -> bool:
        login_data = {
            "username": self.username,
            "password": self.password
        }
        resp = requests.post(f"{self.base_url}/login", json=login_data)
        self.token = resp.json()["data"]["access_token"]
        return True
class ProdTokenManager(TokenManager):
    """生產(chǎn)環(huán)境Token管理器 - 更嚴(yán)格的安全控制"""
    def get_token(self) -> str:
        # 生產(chǎn)環(huán)境可能需要更復(fù)雜的認(rèn)證流程
        raise Exception("生產(chǎn)環(huán)境請(qǐng)使用正式的認(rèn)證流程")
    def refresh_token(self) -> bool:
        raise Exception("生產(chǎn)環(huán)境請(qǐng)使用正式的認(rèn)證流程")
class TokenManagerFactory:
    """Token管理器工廠"""
    @staticmethod
    def create_manager(env: str, **kwargs) -> TokenManager:
        if env == "dev":
            return DevTokenManager()
        elif env == "test":
            return TestTokenManager(kwargs['base_url'], kwargs['username'], kwargs['password'])
        elif env == "prod":
            return ProdTokenManager()
        else:
            raise ValueError(f"不支持的環(huán)境: {env}")
# 使用示例
token_manager = TokenManagerFactory.create_manager(
    "test", 
    base_url="https://test.api.com",
    username="testuser", 
    password="testpass"
)
token = token_manager.get_token()
headers = {"Authorization": f"Bearer {token}"}
response = requests.get("https://test.api.com/api/data", headers=headers)

優(yōu)缺點(diǎn)分析:

優(yōu)點(diǎn):支持多環(huán)境,擴(kuò)展性強(qiáng),符合開(kāi)閉原則;

缺點(diǎn):實(shí)現(xiàn)相對(duì)復(fù)雜,過(guò)度設(shè)計(jì)對(duì)于簡(jiǎn)單項(xiàng)目。

解決方案9

解決方案9:終極方案 - 封裝支持自動(dòng)刷新的AuthClient。

核心思路:

設(shè)計(jì)一個(gè) AuthHttpClient 類,具備以下能力:

  • 自動(dòng)登錄獲取 Token;
  • 請(qǐng)求時(shí)自動(dòng)攜帶 Token;
  • 檢測(cè) 401 錯(cuò)誤,自動(dòng)刷新;
  • 刷新失敗則重新登錄;
  • Token 持久化(文件或內(nèi)存);
  • 線程安全,支持并發(fā)。

代碼實(shí)現(xiàn):???????

import requests
import json
import os
import time
from typing import Optional, Dict, Any
# =======================
# ?? 認(rèn)證客戶端(支持自動(dòng)刷新)
# =======================
class AuthHttpClient:
    def __init__(
        self,
        base_url: str,
        login_url: str = "/auth/login",
        refresh_url: str = "/auth/refresh",
        username: str = "",
        password: str = "",
        token_file: str = "cache/token.json"
    ):
        self.base_url = base_url.rstrip("/")
        self.login_url = login_url
        self.refresh_url = refresh_url
        self.username = username
        self.password = password
        self.token_file = token_file
        self.session = requests.Session()
        # 確保緩存目錄存在
        os.makedirs(os.path.dirname(token_file), exist_ok=True)
    def _save_token(self, token: str, refresh_token: str, expires_in: int):
        """保存 Token 到本地文件"""
        data = {
            "access_token": token,
            "refresh_token": refresh_token,
            "expires_at": time.time() + expires_in - 60,  # 提前60秒過(guò)期
            "username": self.username
        }
        with open(self.token_file, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2)
    def _load_token(self) -> Optional[Dict[str, Any]]:
        """從文件加載 Token"""
        if not os.path.exists(self.token_file):
            return None
        try:
            with open(self.token_file, "r", encoding="utf-8") as f:
                return json.load(f)
        except:
            return None
    def _is_token_expired(self) -> bool:
        """檢查 Token 是否過(guò)期"""
        token_data = self._load_token()
        if not token_data:
            return True
        return time.time() >= token_data.get("expires_at", 0)
    def login(self) -> bool:
        """登錄并獲取 Token"""
        try:
            resp = self.session.post(
                f"{self.base_url}{self.login_url}",
                json={"username": self.username, "password": self.password}
            )
            if resp.status_code == 200:
                data = resp.json()
                token = data["data"]["access_token"]
                refresh_token = data["data"]["refresh_token"]
                expires_in = data["data"].get("expires_in", 1800)  # 默認(rèn)30分鐘
                self._save_token(token, refresh_token, expires_in)
                self.session.headers.update({"Authorization": f"Bearer {token}"})
                print(f"? 登錄成功,Token 已保存")
                return True
            else:
                print(f"? 登錄失敗: {resp.text}")
                return False
        except Exception as e:
            print(f"? 登錄異常: {e}")
            return False
    def refresh_token(self) -> bool:
        """刷新 Token"""
        token_data = self._load_token()
        if not token_data:
            return False
        try:
            resp = self.session.post(
                f"{self.base_url}{self.refresh_url}",
                json={"refresh_token": token_data["refresh_token"]}
            )
            if resp.status_code == 200:
                data = resp.json()
                new_token = data["data"]["access_token"]
                new_refresh = data["data"]["refresh_token"]
                expires_in = data["data"].get("expires_in", 1800)
                self._save_token(new_token, new_refresh, expires_in)
                self.session.headers.update({"Authorization": f"Bearer {new_token}"})
                print(f"?? Token 刷新成功")
                return True
            else:
                print(f"? Token 刷新失敗: {resp.text}")
                return False
        except Exception as e:
            print(f"? 刷新異常: {e}")
            return False
    def ensure_auth(self) -> bool:
        """確保當(dāng)前有有效 Token(自動(dòng)登錄或刷新)"""
        if self._is_token_expired():
            print("?? Token 已過(guò)期,嘗試刷新...")
            if not self.refresh_token():
                print("?? 刷新失敗,正在重新登錄...")
                return self.login()
        else:
            # 加載有效 Token
            token_data = self._load_token()
            self.session.headers.update({"Authorization": f"Bearer {token_data['access_token']}"})
            print("?? 使用現(xiàn)有 Token")
        return True
    def request(self, method: str, url: str, **kwargs) -> requests.Response:
        """重寫(xiě) request,自動(dòng)處理認(rèn)證"""
        full_url = self.base_url + ("" if url.startswith("/") else "/") + url
        # 確保認(rèn)證有效
        if not self.ensure_auth():
            raise Exception("認(rèn)證失敗,無(wú)法繼續(xù)請(qǐng)求")
        try:
            resp = self.session.request(method, full_url, **kwargs)
            # 如果返回 401,可能是刷新失敗,嘗試重新登錄
            if resp.status_code == 401:
                print("?? 收到 401,嘗試重新登錄...")
                self.login()
                # 重新發(fā)送請(qǐng)求
                resp = self.session.request(method, full_url, **kwargs)
            return resp
        except Exception as e:
            print(f"? 請(qǐng)求異常: {e}")
            raise
    # 便捷方法
    def get(self, url, **kwargs):
        return self.request("GET", url, **kwargs)
    def post(self, url, **kwargs):
        return self.request("POST", url, **kwargs)
    def put(self, url, **kwargs):
        return self.request("PUT", url, **kwargs)
    def delete(self, url, **kwargs):
        return self.request("DELETE", url, **kwargs)

優(yōu)缺點(diǎn)分析:

這個(gè)方案融合了前面所有方案的優(yōu)點(diǎn),提供了最完善、最健壯的Token自動(dòng)刷新機(jī)制。?

以上就是Python接口自動(dòng)化中Token過(guò)期的9種解決方案的詳細(xì)內(nèi)容,更多關(guān)于Python接口自動(dòng)化Token過(guò)期的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

临澧县| 安泽县| 轮台县| 崇左市| 馆陶县| 祁阳县| 北辰区| 长乐市| 安宁市| 牟定县| 西昌市| 阆中市| 长葛市| 石台县| 阳山县| 蓝田县| 个旧市| 青神县| 休宁县| 芷江| 霸州市| 巩义市| 阳原县| 嫩江县| 青龙| 临城县| 榕江县| 玉环县| 拉孜县| 凌源市| 黎平县| 阳泉市| 安乡县| 千阳县| 高碑店市| 安庆市| 彰武县| 渑池县| 中宁县| 龙口市| 阳东县|