维护用户会话而无需将用户保存在 django 身份验证系统中

Posted

技术标签:

【中文标题】维护用户会话而无需将用户保存在 django 身份验证系统中【英文标题】:Maintaining user session without having user saved in django authentication system 【发布时间】:2020-01-30 08:46:12 【问题描述】:

我有一个小型 Django 应用程序,其中用户需要通过安装在 Apache 上的 Webgate 插件进行外部身份验证。 Webgate 将使用托管在 Intranet 上的其他企业身份验证系统来对进入我的 django 站点的用户进行身份验证。一旦企业身份验证系统对用户进行身份验证,它将使用标头 REMOTE-USER 将他们重定向到我们的站点。此标头将包含用户名。我只需要确保该标头中提到的用户能够访问该站点,即使它在我的站点上不存在,相信他已经通过身份验证。 为此,我正在使用

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.contrib.auth.middleware.RemoteUserMiddleware',
]
AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.RemoteUserBackend',
)

这行得通。但它会在网站中创建用户。要求是用户不得存在于我们的数据库中。它们可以存在于内存中。他们需要开会 这在 Django 中甚至可能吗?如果是,我怎么能做到这一点? 我尝试将 AllowAllUsersRemoteUserBackend 子类化如下并覆盖身份验证方法以返回仅在内存中而不是在 DB 中的用户。

class AllowAllUsersRemoteUserBackendWithoutCreatingUnknownUsers(AllowAllUsersRemoteUserBackend):

    create_unknown_user = False

    def authenticate(self, request, remote_user):
        return User(username = remote_user)

AUTHENTICATION_BACKENDS = (
    'polls.middleware.authentication.AllowAllUsersRemoteUserBackendWithoutCreatingUnknownUsers',
)

但是我得到了一个例外

ValueError at /polls/
Cannot force an update in save() with no primary key.
Request Method: GET
Request URL:    http://localhost:8001/polls/
Django Version: 2.1.7
Exception Type: ValueError
Exception Value:    
Cannot force an update in save() with no primary key.
Exception Location: C:\Users\rizwan_shaikh\Envs\sessiontest\lib\site-packages\django\db\models\base.py in _save_table, line 803
Python Executable:  C:\Users\rizwan_shaikh\Envs\sessiontest\Scripts\python.exe
Python Version: 3.8.1
Python Path:    
['C:\\ApplicationCode\\Kenstar\\sessiontest\\djangosessiontestwithwsgi',
 'C:\\Users\\rizwan_shaikh\\Envs\\sessiontest\\Scripts\\python38.zip',
 'C:\\Users\\rizwan_shaikh\\Envs\\sessiontest\\DLLs',
 'C:\\Users\\rizwan_shaikh\\Envs\\sessiontest\\lib',
 'C:\\Users\\rizwan_shaikh\\Envs\\sessiontest\\Scripts',
 'c:\\softwares\\pythoninstallation\\Lib',
 'c:\\softwares\\pythoninstallation\\DLLs',
 'C:\\Users\\rizwan_shaikh\\Envs\\sessiontest',
 'C:\\Users\\rizwan_shaikh\\Envs\\sessiontest\\lib\\site-packages',
 '/app',
 '/usr/local/lib/python3.7/site-packages']
Server time:    Thu, 30 Jan 2020 08:37:34 +0000

现在我没有在 apache 上运行我的代码,而是在 django 开发服务器上运行。我正在通过另一个中间件手动添加包含用户名的标头。目标是让基本的东西正常工作,然后将代码部署到 apache。

【问题讨论】:

【参考方案1】:

我可以通过使用以下设置来解决这个问题。

MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',  
'corsheaders.middleware.CorsMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'module.CheckIDMHeaderMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
AUTH_USER_MODEL = 'auth.User'

REST_FRAMEWORK = 
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': [],
    'UNAUTHENTICATED_USER': None,

必须创建我自己的用户类。

class User:
username = ''
user_permissions = []
is_active = True

def get_user_permissions(self):
    return self.user_permissions

def __init__(self, username):
    self.username = username
    self.is_active = True

在 CheckIDMHeaderMiddleware 的 call 方法中,我正在读取包含从 webgate 插件获得的用户名的标头。使用该用户名,我创建了一个用户对象并在它的会话中进行设置。

def __call__(self, request):
    # some code skipped to keep the answer simple.
    ....
    username = request.META[header_name]
    user = User(username = username)
    request.user = user
    response = self.get_response(request)
    return response
    ....
    # some code skipped to keep the answer simple.
            

....

【讨论】:

以上是关于维护用户会话而无需将用户保存在 django 身份验证系统中的主要内容,如果未能解决你的问题,请参考以下文章

django 会话密钥在身份验证时更改

注销后如何保存购物车数据的会话,以便用户在 Django 中再次登录时可以找到它们?

使用带有令牌而不是 Cookie 的 Django 会话框架?

Django 会话session

在 Django 中,是不是可以从自定义标签中访问当前用户会话?

Django 会话表单(临时保存表单)