token失效后再次请求获取新token

Neutron日志中认证失败警告解析
本文探讨了Neutron日志中频繁出现的认证失败警告现象,分析表明这些失败源于过期的token尝试访问服务。文章深入研究了keystoneclient的认证流程,解释了如何通过重新认证来确保服务正常运行。
部署运行你感兴趣的模型镜像

在neutron日志中发现经常有认证失败warning,但是认证失败并不会导致服务请求报错,这是什么原因呢?

在keystonemiddleware中打印认证的token,发现认证失败的token都是已经过期的token,这些过期的token认证时肯定是失败的,所以返回认证失败。在认证失败后,服务会再次请求获取新的token,再用新的token进行验证,这样验证就可以通过了,也就不影响服务的正常运行了。代码分析如下:

在上一篇中blog《rest api请求时token的注入》介绍了neutronclient发起http请求的过程。httpclient实际上是SessionClient,因为创建client时传进的session总不是为None,是 keystoneclient.session.Session,所以调用的do_request函数是SessionClient的do_request函数。

class SessionClient(adapter.Adapter):

    def request(self, *args, **kwargs):
        kwargs.setdefault('authenticated', False)
        kwargs.setdefault('raise_exc', False)

        content_type = kwargs.pop('content_type', None) or 'application/json'

        headers = kwargs.setdefault('headers', {})
        headers.setdefault('Accept', content_type)

        try:
            kwargs.setdefault('data', kwargs.pop('body'))
        except KeyError:
            pass

        if kwargs.get('data'):
            headers.setdefault('Content-Type', content_type)

        resp = super(SessionClient, self).request(*args, **kwargs)
        return resp, resp.text

    def do_request(self, url, method, **kwargs):
        kwargs.setdefault('authenticated', True)
        self._check_uri_length(url)
        return self.request(url, method, **kwargs)
do_request函数调用request函数,request函数又调用父类的request函数,父类为adapter.Adapter,其request函数为:

class Adapter(object):
    def request(self, url, method, **kwargs):
        endpoint_filter = kwargs.setdefault('endpoint_filter', {})
        self._set_endpoint_filter_kwargs(endpoint_filter)

        if self.endpoint_override:
            kwargs.setdefault('endpoint_override', self.endpoint_override)

        if self.auth:
            kwargs.setdefault('auth', self.auth)
        if self.user_agent:
            kwargs.setdefault('user_agent', self.user_agent)
        if self.connect_retries is not None:
            kwargs.setdefault('connect_retries', self.connect_retries)
        if self.logger:
            kwargs.setdefault('logger', self.logger)

        return self.session.request(url, method, **kwargs)

在Adapter函数中,又调用session.request函数,此处session和SessionClient的session是一样的,为keystoneclient.session.Session。其request函数为:

class Session(object):
    @utils.positional(enforcement=utils.positional.WARN)
    def request(self, url, method, json=None, original_ip=None,
                user_agent=None, redirect=None, authenticated=None,
                endpoint_filter=None, auth=None, requests_auth=None,
                raise_exc=True, allow_reauth=True, log=True,
                endpoint_override=None, connect_retries=0, logger=_logger,
                **kwargs):
        ........
        send = functools.partial(self._send_request,
                                 url, method, redirect, log, logger,
                                 connect_retries)
        
        resp = send(**kwargs)

        # handle getting a 401 Unauthorized response by invalidating the plugin
        # and then retrying the request. This is only tried once.
        if resp.status_code == 401 and authenticated and allow_reauth:            
            if self.invalidate(auth):
                auth_headers = self.get_auth_headers(auth)
                if auth_headers is not None:
                    headers.update(auth_headers)                    
                    resp = send(**kwargs)

        if raise_exc and resp.status_code >= 400:
            logger.debug('Request returned failure status: %s',
                         resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
从request函数可以看到在第一次send后获取返回的response(resp),如果返回的代码为401,并且authenticated,允许重新认证,则就会进行第二次认证,在发起第二次请求处理,如果第二次请求失败,则就再返回失败结果。












您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

### 解决方案概述 为了处理前端项目中Token过期时取消其他请求的情况,可以采用拦截器模式配合Axios库实现。通过设置全局响应拦截器,在检测到Token失效的情况下执行特定操作,比如清除未完成的请求队列并提示用户重新登录。 ### 实现细节 #### 创建请求实例与配置默认参数 基于现有项目的`request.js`文件中的代码片段[^2]: ```javascript // frontend/src/utils/request.js import axios from 'axios'; const FEBS_REQUEST = axios.create({ baseURL: 'http://127.0.0.1:9527/', responseType: 'json', validateStatus(status) { return status >= 200 && status < 300; // 修改为接受更多成功状态码范围 } }); ``` #### 添加请求与响应拦截器 增强上述创建好的 Axios 实例功能,加入对Token验证失败场景的支持: ```javascript // 请求前添加Token至header FEBS_REQUEST.interceptors.request.use(config => { const token = localStorage.getItem('access_token'); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }, error => Promise.reject(error)); // 响应后处理Token过期情况 FEBS_REQUEST.interceptors.response.use(response => response, async error => { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { await refreshToken(); // 尝试刷Token // 刷成功则重发原请求 return FEBS_REQUEST(originalRequest); } catch (_error) { // 如果刷也失败,则注销当前会话并跳转回登录页 logout(); window.location.href = '/login'; } // 取消所有挂起的API调用 cancelAllPendingRequests(); throw new Error("Token has expired or is invalid"); } return Promise.reject(error); }); function cancelAllPendingRequests() { // 这里假设有一个管理待处理请求的方法 } ``` #### 注册取消令牌源 为了让每个请求都能被单独控制其生命周期,可以在发起请求之前为其分配一个唯一的取消令牌源对象,并将其存储在一个集中式的列表内以便后续查找和清理: ```javascript const pendingRequests = []; export function addPendingRequest(cancelTokenSource) { pendingRequests.push(cancelTokenSource); // 设置超时自动移除机制防止内存泄漏 setTimeout(() => removePendingRequest(cancelTokenSource), 60 * 1000); // 存活时间设为一分钟 } export function removePendingRequest(cancelTokenSource) { const index = pendingRequests.indexOf(cancelTokenSource); if (index > -1) pendingRequests.splice(index, 1); } export function cancelAllPendingRequests() { while(pendingRequests.length){ let source = pendingRequests.pop(); source.cancel('Operation canceled due to token expiration.'); } } ``` 每次发出请求时都要关联对应的取消令牌源: ```javascript async function fetchData(url, params={}) { const CancelToken = axios.CancelToken; const source = CancelToken.source(); addPendingRequest(source); try{ const result = await FEBS_REQUEST.get(url,{ params, cancelToken: source.token }); removePendingRequest(source); return result.data; }catch(e){ console.error(`Error fetching data from ${url}`, e.message || e); throw e; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值