You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中Keystone/OpenStack API认证与会话共享方案问询

解决方案:Django中共享Keystone客户端会话的几种方法

你遇到的核心问题是Python对象无法序列化存入Django的request.session,所以得换思路,不用直接存客户端对象,而是通过以下几种方式实现跨视图共享认证状态:


1. 存储认证凭证,按需重建客户端

把Keystone认证所需的字符串参数(而非客户端对象)存在request.session里,在需要调用API的视图中重新初始化客户端。这种方式最简单,不需要额外依赖:

登录视图(存储凭证)

def keystone_login(request):
    # 这里先完成用户的Django登录验证,再初始化Keystone认证
    auth_params = {
        'auth_url': 'https://my.keystone.com:5000/v3',
        'user_id': 'myuserid',
        'password': 'mypassword',
        'project_id': 'myprojectid'
    }
    # 存到session,这些都是可序列化的字符串
    request.session['keystone_auth_params'] = auth_params
    return redirect('dashboard')

其他业务视图(重建客户端)

from keystoneauth1.identity import v3
from keystoneauth1 import session
from keystoneclient.v3 import client

def manage_projects(request):
    # 从session取出凭证
    auth_params = request.session.get('keystone_auth_params')
    if not auth_params:
        return redirect('keystone_login')
    
    # 重建认证会话和客户端
    auth = v3.Password(**auth_params)
    sess = session.Session(auth=auth)
    keystone = client.Client(session=sess, include_metadata=True)
    
    # 接下来就可以用keystone调用API了,比如获取项目列表
    projects = keystone.projects.list()
    # ...后续业务逻辑

2. 用Django缓存存储会话对象

如果不想每次都重建客户端,可以用Django缓存(比如Redis、Memcached)存储session.Session对象——缓存支持存储非序列化Python对象(部分后端会自动用pickle序列化),只要给每个用户设置唯一缓存键即可:

登录视图(存会话到缓存)

from django.core.cache import cache

def keystone_login(request):
    # 初始化认证会话
    auth = v3.Password(auth_url='https://my.keystone.com:5000/v3',
                       user_id='myuserid',
                       password='mypassword',
                       project_id='myprojectid')
    sess = session.Session(auth=auth)
    
    # 用用户ID作为缓存键,设置过期时间(和Keystone Token有效期匹配)
    cache_key = f"keystone_sess_{request.user.id}"
    cache.set(cache_key, sess, timeout=3600)
    
    # 同时把基础凭证存session,用于缓存失效时重建
    request.session['keystone_auth_params'] = {
        'auth_url': 'https://my.keystone.com:5000/v3',
        'user_id': 'myuserid',
        'password': 'mypassword',
        'project_id': 'myprojectid'
    }
    return redirect('dashboard')

业务视图(从缓存取会话)

def manage_projects(request):
    cache_key = f"keystone_sess_{request.user.id}"
    sess = cache.get(cache_key)
    
    if not sess:
        # 缓存失效,从session取凭证重建
        auth_params = request.session.get('keystone_auth_params')
        if not auth_params:
            return redirect('keystone_login')
        auth = v3.Password(**auth_params)
        sess = session.Session(auth=auth)
        cache.set(cache_key, sess, 3600)
    
    # 初始化客户端
    keystone = client.Client(session=sess, include_metadata=True)
    # ...业务逻辑

3. 自定义中间件,自动注入客户端

写一个Django中间件,在每个请求处理前自动初始化Keystone客户端并绑定到request对象上,这样所有视图都能直接使用,不用重复写初始化代码:

编写中间件(myapp/middleware.py)

from keystoneauth1.identity import v3
from keystoneauth1 import session
from keystoneclient.v3 import client
from django.core.cache import cache
from django.shortcuts import redirect

class KeystoneClientMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # 仅对已登录用户处理
        if request.user.is_authenticated:
            cache_key = f"keystone_sess_{request.user.id}"
            sess = cache.get(cache_key)
            
            if not sess:
                # 从session取凭证重建会话
                auth_params = request.session.get('keystone_auth_params')
                if auth_params:
                    auth = v3.Password(**auth_params)
                    sess = session.Session(auth=auth)
                    cache.set(cache_key, sess, 3600)
                    # 绑定客户端到request
                    request.keystone = client.Client(session=sess, include_metadata=True)
                else:
                    request.keystone = None
            else:
                request.keystone = client.Client(session=sess, include_metadata=True)
        else:
            request.keystone = None

        response = self.get_response(request)
        return response

注册中间件(settings.py)

在MIDDLEWARE列表末尾添加:

MIDDLEWARE = [
    # ...其他中间件
    'myapp.middleware.KeystoneClientMiddleware',
]

视图中直接使用

def manage_projects(request):
    if not request.keystone:
        return redirect('keystone_login')
    
    # 直接用request.keystone调用API
    projects = request.keystone.projects.list()
    # ...业务逻辑

4. 直接存储Token,调用REST API

如果不想用python-keystoneclient,可以直接调用Keystone的REST API:登录时获取Token并存入session,后续请求API时带上Token头即可:

登录视图(获取Token)

import requests

def keystone_login(request):
    auth_url = 'https://my.keystone.com:5000/v3/auth/tokens'
    payload = {
        "auth": {
            "identity": {
                "methods": ["password"],
                "password": {
                    "user": {
                        "id": "myuserid",
                        "password": "mypassword"
                    }
                }
            },
            "scope": {
                "project": {
                    "id": "myprojectid"
                }
            }
        }
    }
    response = requests.post(auth_url, json=payload)
    if response.status_code == 201:
        # 提取Token和必要参数存session
        request.session['keystone_token'] = response.headers['X-Subject-Token']
        request.session['keystone_api_url'] = 'https://my.keystone.com:5000/v3'
        return redirect('dashboard')

业务视图(调用REST API)

import requests

def create_project(request):
    token = request.session.get('keystone_token')
    api_url = request.session.get('keystone_api_url')
    if not token or not api_url:
        return redirect('keystone_login')
    
    headers = {
        'X-Auth-Token': token,
        'Content-Type': 'application/json'
    }
    payload = {
        "project": {
            "name": "new-test-project",
            "description": "测试项目",
            "enabled": True
        }
    }
    response = requests.post(f"{api_url}/projects", json=payload, headers=headers)
    # 处理响应结果

内容的提问来源于stack exchange,提问作者Crouch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 01:01:19