我的Django应用程序有两种类型的用户,每种用户都有自己的身份验证机制:普通用户使用用户名和密码登录,员工使用django-cas-client通过公司的SSO服务器登录。< / p>
为此,我需要两个用户表:一个用于普通用户,一个用于员工。单个表不会删除它,因为用户名可能会重叠。也就是说,用户名对于普通用户来说是唯一的,对于员工来说是唯一的,但普通用户和员工可能具有相同的用户名。
类似问题,例如this one,this one和this one并不适用于我的情况,因为在我的情况下,不同的用户类型可以使用相同的用户名,同时仍然是不同的用户。不幸的是,我对CAS服务器返回的用户名没有任何影响。
我创建了两个配置文件模型,而不是创建两个用户模型,通过OneToOneField链接到用户模型:
from django.conf import settings
class RegularUser(models.Model)
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='regular_user')
nickname = models.CharField(max_length=255, blank=True)
class Employee(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='employee')
first_name = models.CharField(max_length=255, blank=True)
full_name = models.CharField(max_length=255, blank=True)
NR = models.CharField(max_length=255, blank=True)
email = models.CharField(max_length=255, blank=True)
然后我使用django-cas-client CAS Response Callback自动使用通过LDAP检索的employee_number填充新创建的CAS用户:
from django.contrib.auth import get_user_model
from .models import Employee
def callback(tree):
username = tree[0][0].text
user, created = get_user_model().objects.get_or_create(username=username)
employee, created = Employee.objects.get_or_create(user=user)
try:
(
employee.first_name,
employee.full_name,
employee.NR,
employee.email
) = search_ldap(username)
employee.save()
except LDAPError:
pass
对于那些感兴趣的人,这是search_ldap()
函数的实现:
def search_ldap(username):
result = ()
baseDN = "o=Our Organization Name,c=NL"
searchFilter = '(uid={})'.format(username)
attributes = ['givenName', 'cn', 'employeeNumber', 'mail']
try:
server = Server('ldap.example.com', use_ssl=True)
conn = Connection(server, auto_bind=True)
conn.search(baseDN, searchFilter, attributes=attributes)
for a in attributes:
result += (conn.response[0]['attributes'][a][0], )
except Exception:
raise LDAPError('Error in LDAP query')
return result
最终结果是只有通过CAS服务器登录的用户才拥有employee
属性。我还没有自动创建常规用户,但当然只有他们才会拥有regular_user
属性。要记录用户,我在urls.py
中定义了两个单独的登录页面:
import cas.views
import django.contrib.auth.views
from django.conf.urls import include, url
from .views import login, logout
urlpatterns = [
url(r'^login/regular/$', django.contrib.auth.views.login, name='login_regular'),
url(r'^logout/regular/$', django.contrib.auth.views.logout, name='logout_regular'),
url(r'^login/sso/$',cas.views.login, name='login_sso'),
url(r'^logout/sso/$',cas.views.logout, name='logout_sso'),
url(r'^login/$', login, name='login'),
url(r'^logout/$', logout, name='logout'),
]
如您所见,还有一般的login
和logout
网址。我已经实现了以下这些观点:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
def login(request):
param = request.META['QUERY_STRING']
return render(request, 'login.html', {
'param': param,
})
@login_required
def logout(request):
if hasattr(request.user, 'employee'):
return redirect('logout_sso')
else:
return redirect('logout_regular')
常规登录模板只显示指向login_sso
网址和login_regular
网址的链接。 logout
视图始终返回重定向到正确的注销网址。
此代码完美且按预期工作,有一个主要问题:有一天,CAS服务器可能会返回一个员工用户名,通过纯粹的coincedence,与现有的普通用户相同。 CAS响应回调将很乐意检索现有用户并启动employee
个人资料。最终结果是此用户现在同时具有regular_user
属性和employee
属性。更糟糕的是,原始普通用户的帐户现在已被具有相同用户名的员工劫持。我想让普通用户和具有相同用户名的员工和平共处。我怎么能做到这一点?