Django開發文檔-域用戶集成登錄
項目概述:
一般在企業中,用戶以WINDOWS的域用戶統一的管理,所以以Django快速開發的應用,不得不集成AD域登錄。
網上一般採用django-python3-ldap的庫來做集成登錄,但是本方案中需要同時使用域用戶登錄以及站點用戶登錄的功能。所以我們直接改寫django的ModelBackend類以及User類來實現。
實現功能:
User表中增加一個 是否是域用戶的欄位,如果登錄用戶是域用戶則採用ldap認證,如果不是域用戶還採用Django本身的驗證。
實現分析:
一、User表中如何增加欄位
Django的auth_user表的欄位是由django.contrib.auth.models中的User類控制的。
#Django源碼 class User(AbstractUser): """ Users within the Django authentication system are represented by this model. Username and password are required. Other fields are optional. """ class Meta(AbstractUser.Meta): swappable = 'AUTH_USER_MODEL'
我們需要對User類進行改寫來實現增加欄位的功能
步驟一
首先我們需要新建一個my_user_auth的app
python manage.py startapp my_user_auth
步驟二
在my_user_auth的models文件中對User進行重新定義。裡面增加了一個is_ad_account的欄位用來標識是否是域用戶。
from django.contrib.auth.models import AbstractUser class User(AbstractUser): """ Users within the Django authentication system are represented by this model. Username and password are required. Other fields are optional. """ is_ad_account=models.BooleanField('啟用域用戶登錄', default=False) cn_name = models.CharField(('中文名'), max_length=30, blank=True) en_name = models.CharField(('英文名'), max_length=150, blank=True) def full_name(self): # 計算欄位要顯示在修改頁面中只能定義在只讀欄位中 return self.cn_name+'#'+self.en_name class Meta(AbstractUser.Meta): swappable = 'AUTH_USER_MODEL'
步驟三
在admin中註冊自定義的User類
from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .models import User class User_exAdmin(UserAdmin): #查詢欄位 search_fields = ('username', 'first_name', 'last_name', 'email') #編輯顯示欄位,我們在其中添加我們剛才創建的'cn_name', 'en_name', 'is_ad_account fieldsets = ( (None, {'fields': ('username', 'password')}), (('Personal info'), {'fields': ('cn_name', 'en_name', 'email','is_ad_account')}), (('Permissions'), { 'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'), }), (('Important dates'), {'fields': ('last_login', 'date_joined')}), ) add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('username', 'password1', 'password2'), }), ) #顯示欄位 list_display = ('username', 'email', 'cn_name', 'en_name', 'is_staff') admin.site.register(User, User_exAdmin)
步驟四
修改setting文件中
1、添加INSTALLED_APPS
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', #你自定義登錄的app名 'my_user_auth', ]
2、添加AUTH_USER_MODEL,將系統的User類替換為你寫的User類。
AUTH_USER_MODEL = 'my_user_auth.User'
3、運行makemigrations、migrate。
但是首先要確保是你的Django項目還沒有運行任何的makemigrations、migrate。那會導致系統的錯誤。
4、完成後打開資料庫,可以看到my_user_auth_user的表數據表。那麼恭喜你,自定義User已經成功了。
二、修改控制登錄邏輯的ModelBackend類
User類修改完成後,只需要將登錄邏輯修改完成,那麼我們的功能也就完成了
先來研究下Django中ModelBackend的源碼
步驟一
我們在剛剛新建的app【my_user_auth】中創建一個【backends.py】
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.backends import UserModel from my_user_auth.ad_auth import ldap_auth class UserBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): if username is None: username = kwargs.get(UserModel.USERNAME_FIELD) if username is None or password is None: return try: user = UserModel._default_manager.get_by_natural_key(username) except UserModel.DoesNotExist: # Run the default password hasher once to reduce the timing # difference between an existing and a nonexistent user (#20760). UserModel().set_password(password) else: if user.is_ad_account: if ldap_auth(user.username,password) and self.user_can_authenticate(user): print("域用戶登陸成功",user.username) return user else: if user.check_password(password) and self.user_can_authenticate(user): print("網站帳號登陸成功",user.username) return user
步驟二
修改setting文件中AUTHENTICATION_BACKENDS將‘django.contrib.auth.backends.ModelBackend’修改為‘my_user_auth.backends.UserBackend’
AUTHENTICATION_BACKENDS = ['my_user_auth.backends.UserBackend']
步驟三
基於ldap3來ldap認證的程式碼
from ldap3 import Server, Connection, ALL, SUBTREE, ServerPool #域伺服器IP,輔域就行了 LDAP_SERVER_POOL = ["192.168.3.2"] #LDAP服務埠 LDAP_SERVER_PORT = 389 def ldap_auth(username, password): ldap_server_pool = ServerPool(LDAP_SERVER_POOL) #用戶以[email protected]的形式錄入 ad_user=f"""{username}@abc.net""" conn = Connection(ldap_server_pool, user=ad_user, password=password, check_names=True, lazy=False, raise_exceptions=False) conn.open() conn.bind() if conn.result["result"]==0: conn.unbind() return True else: conn.unbind() return False
步驟四
完成後請在用戶維護介面勾選啟用域用戶登錄,那麼這個用戶就可以用域用戶進行認證了。