目錄:Django其他篇
05:ModelForm 數據驗證 & 生成html & 數據庫操作
1.1 配置ldap認證
參考博客:https://www.cnblogs.com/dreamer-fish/p/5474289.html
官網地址:https://pypi.org/project/django-auth-ldap/1.3.0/
1、django使用ldap認證需要安裝下面兩個模塊(這里是在linux下測試的)
1.安裝Python-LDAP(python_ldap-2.4.25-cp27-none-win_amd64.whl)pip install python_ldap-2.4.25-cp27-none-win_amd64.whl
2.安裝django-auth-ldap(django-auth-ldap-1.2.8.tar.gz)(下載:https://pypi.python.org/pypi/django-auth-ldap),Windows下也可以使用 python setup.py install
3. 安裝成功后運行命令,運行成功表示安裝成功: from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion, GroupOfNamesType
2、LDAP用戶驗證基本原理
1. 每個用戶在LDAP系統中有一個唯一的DN值,例如配置文件中默認的admin用戶在LDAP中的DN值是uid=admin,ou=system,dc=eoncloud,dc=com
,
2. 其中eoncloud.com是域名,system是組名,admin是用戶名,有些LDAP用cn而不是uid來生成DN
3. 在這種系統中admin的DN看起來像這樣cn=admin,ou=system,dc=eoncloud,dc=com
,無論是uid還是cn或是別的前綴,django-ldap-auth都是用dn來驗證用戶和獲取用戶信息的
4. 假設用戶輸入的帳號及密碼是: test, password,django-auth-ldap有2個方式來獲取用戶的DN
1)使用AUTH_LDAP_USER_DN_TEMPLATE提供的模板生成DN.如uid=%(user)s,ou=users,dc=eoncloud,dc=com,
其中%(user)s會被替換成用戶名,這樣最終的DN就是 uid=test,ou=users,dc=eonclooud,dc=com.
2)使用AUTH_LDAP_GROUP_SEARCH.如果沒有配置AUTH_LDAP_USER_DN_TEMPLATE,那么django-auth-ldap會使用
AUTH_LDAP_BIND_DN和AUTH_LDAP_BIND_PASSWORD提供的dn與密碼根據AUTH_LDAP_GROUP_SEARCH提供的查詢條件去查找test用戶,
如果查不到,驗證失敗,如果查到用戶,就使用返回的數據生成test的DN.
利用第2步生成DN值與密碼嘗試訪問LDAP系統,如果訪問成功,則驗證共過,否則驗證失敗.
3、基本配置使用

# -*- coding:utf8 -*- import ldap from django_auth_ldap.config import LDAPSearch, PosixGroupType AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', # 配置為先使用LDAP認證,如通過認證則不再使用后面的認證方式 'django.contrib.auth.backends.ModelBackend', # 同時打開本地認證,因為下游系統的權限和組關系需要用到 ) #默認登錄后打開首頁 LOGIN_REDIRECT_URL = '/' SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 關閉流量器不清空session SESSION_COOKIE_AGE = 60*60*8 # 8小時后session認證過期 base_dn = 'dc=cloud,dc=cn' # 請求的域名后綴為:cloud.cn AUTH_LDAP_SERVER_URI = 'ldap://ldap-qc.intra.cloud.cn' # LDAP系統的地址及端口號 AUTH_LDAP_BIND_DN = 'cn=cloud,ou=admin,dc=ycloud,dc=cn' # 以admin身份查找用戶及相關信息 AUTH_LDAP_BIND_PASSWORD = 'xxxxx' # admin賬號的密碼 AUTH_LDAP_USER_SEARCH = LDAPSearch('ou=People,%s' % base_dn, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # 第一個參數指定查詢目錄,第三個參數是過濾條件,過濾條件可以很復雜,有需要請查看相關文檔. AUTH_LDAP_ALWAYS_UPDATE_USER = True # Default is True,是否登錄后從ldap同步用戶,不進行同步,因為下游的用戶表是什么樣的不能確定,只能確定它也使用郵箱前綴 '''一些其他配置''' # 下游系統不從ldap同步group staff/superuser相關,但需要從ldap驗證用戶是否離職 # AUTH_LDAP_GROUP_SEARCH = LDAPSearch('ou=Group,dc=ldap,dc=ssotest,dc=net', ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)") # AUTH_LDAP_GROUP_TYPE = PosixGroupType(name_attr="cn") # AUTH_LDAP_REQUIRE_GROUP = u"cn=員工,ou=Group,dc=ldap,dc=ssotest,dc=net" # AUTH_LDAP_DENY_GROUP = u"cn=黑名單,ou=Group,dc=ldap,dc=ssotest,dc=net" # AUTH_LDAP_FIND_GROUP_PERMS = True # django從ldap的組權限中獲取權限,這種方式,django自身不創建組,每次請求都調用ldap,下游子系統,我們並不需要讓他同步ldap里的"員工","管理員"這種表,所以不用mirror_groups # AUTH_LDAP_CACHE_GROUPS = True # 如打開FIND_GROUP_PERMS后,才生效,對組關系進行緩存,不用每次請求都調用ldap # AUTH_LDAP_GROUP_CACHE_TIMEOUT = 600 # ### ldap 配置部分END ### #

from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.Login.as_view()), url(r'^home/', views.home, name='home'),

<!DOCTYPE html> <html> <head> <meta charset=utf-8"> <title>登錄</title> <link rel="stylesheet" href="/static/AdminLTE/bootstrap/css/bootstrap.css"> <link rel="stylesheet" href="/static/AdminLTE/fonts/font-awesome.min.css"> <link rel="stylesheet" href="/static/AdminLTE/ionicons/ionicons.css"> <link rel="stylesheet" href="/static/AdminLTE/dist/css/AdminLTE.css"> <style> .errorlist { color: red; } </style> </head> <body class="hold-transition login-page"> <div class="login-box"> <div class="login-logo"> <b>運維工單·平台</b> </div> <div class="login-box-body"> {% if form.errors %} <p class="errorlist">你輸入的用戶名密碼不正確!!</p> {% endif %} <form action="" method="POST">{% csrf_token %} <div class="form-group has-feedback"> <input type="text" class="form-control" name="username" placeholder="用戶名" required> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" class="form-control" name="password" placeholder="密碼" required> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <button class="btn btn-primary btn-block btn-flat" type="submit">登陸</button> </form> <br> </div> </div> </body> </html>
1.2 在django項目中使用

# -*- coding:utf8 -*- import ldap from django_auth_ldap.config import LDAPSearch, PosixGroupType AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', # 配置為先使用LDAP認證,如通過認證則不再使用后面的認證方式 'django.contrib.auth.backends.ModelBackend', # 同時打開本地認證,因為下游系統的權限和組關系需要用到 ) #默認登錄后打開首頁 LOGIN_REDIRECT_URL = '/' SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 關閉流量器不清空session SESSION_COOKIE_AGE = 60*60*8 # 8小時后session認證過期 base_dn = 'dc=cloud,dc=cn' # 請求的域名后綴為:cloud.cn AUTH_LDAP_SERVER_URI = 'ldap://ldap-qc.intra.cloud.cn' # LDAP系統的地址及端口號 AUTH_LDAP_BIND_DN = 'cn=cloud,ou=admin,dc=cloud,dc=cn' # 以admin身份查找用戶及相關信息 AUTH_LDAP_BIND_PASSWORD = 'xxxxx' # admin賬號的密碼 AUTH_LDAP_USER_SEARCH = LDAPSearch('ou=People,%s' % base_dn, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # 第一個參數指定查詢目錄,第三個參數是過濾條件,過濾條件可以很復雜,有需要請查看相關文檔. AUTH_LDAP_ALWAYS_UPDATE_USER = True # Default is True,是否登錄后從ldap同步用戶,不進行同步,因為下游的用戶表是什么樣的不能確定,只能確定它也使用郵箱前綴 '''一些其他配置''' # 下游系統不從ldap同步group staff/superuser相關,但需要從ldap驗證用戶是否離職 # AUTH_LDAP_GROUP_SEARCH = LDAPSearch('ou=Group,dc=ldap,dc=ssotest,dc=net', ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)") # AUTH_LDAP_GROUP_TYPE = PosixGroupType(name_attr="cn") # AUTH_LDAP_REQUIRE_GROUP = u"cn=員工,ou=Group,dc=ldap,dc=ssotest,dc=net" # AUTH_LDAP_DENY_GROUP = u"cn=黑名單,ou=Group,dc=ldap,dc=ssotest,dc=net" # AUTH_LDAP_FIND_GROUP_PERMS = True # django從ldap的組權限中獲取權限,這種方式,django自身不創建組,每次請求都調用ldap,下游子系統,我們並不需要讓他同步ldap里的"員工","管理員"這種表,所以不用mirror_groups # AUTH_LDAP_CACHE_GROUPS = True # 如打開FIND_GROUP_PERMS后,才生效,對組關系進行緩存,不用每次請求都調用ldap # AUTH_LDAP_GROUP_CACHE_TIMEOUT = 600 # ### ldap 配置部分END ### #

from django.conf.urls import url from django.contrib import admin from app01 import views from django.contrib.auth.views import login, logout urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.Login.as_view()), url(r'^home/', views.home, name='home'), url(r'^$',views.index), url(r'^ac_logout/?$', views.logout_view, name="account_logout"), url(r'^change_passwd/?$', views.change_pass, name="change_passwd"), ]

# # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.shortcuts import render,HttpResponse,redirect from django.contrib.auth.views import LoginView from django.contrib.auth import login as auth_login from django.contrib.auth.decorators import login_required from django.contrib.auth import logout from django.contrib.auth.forms import PasswordChangeForm #1、登錄:優先使用ldap認證,settings中配置了 class Login(LoginView): template_name = 'login.html' def form_valid(self, form): auth_login(self.request, form.get_user()) return super(Login, self).form_valid(form) def get_context_data(self, **kwargs): context = super(Login,self).get_context_data(**kwargs) return context #2、注銷 def logout_view(request): logout(request) return redirect('/login') #3、修改密碼:可以修改本地密碼,不是修改ldap密碼 @login_required(login_url='/login') def change_pass(request): form = PasswordChangeForm(user=request.user) if request.method == 'POST': form = PasswordChangeForm(request.user, request.POST) if form.is_valid(): form.save() return redirect('/') return render(request, template_name='change_pass.html', context={'form': form, 'username': request.user.username}) #4、index首頁:登陸后默認返回此頁面 @login_required(login_url='/login') def index(request): return render(request, 'index.html') #5、home頁面:只有登錄才返回,否則返回到login頁面 @login_required(login_url='/login') def home(request): return HttpResponse('home')

<!DOCTYPE html> <html> <head> <meta charset=utf-8"> <title>登錄</title> <link rel="stylesheet" href="/static/AdminLTE/bootstrap/css/bootstrap.css"> <link rel="stylesheet" href="/static/AdminLTE/fonts/font-awesome.min.css"> <link rel="stylesheet" href="/static/AdminLTE/ionicons/ionicons.css"> <link rel="stylesheet" href="/static/AdminLTE/dist/css/AdminLTE.css"> <style> .errorlist { color: red; } </style> </head> <body class="hold-transition login-page"> <div class="login-box"> <div class="login-logo"> <b>運維工單·平台</b> </div> <div class="login-box-body"> {% if form.errors %} <p class="errorlist">你輸入的用戶名密碼不正確!!</p> {% endif %} <form action="" method="POST">{% csrf_token %} <div class="form-group has-feedback"> <input type="text" class="form-control" name="username" placeholder="用戶名" required> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" class="form-control" name="password" placeholder="密碼" required> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <button class="btn btn-primary btn-block btn-flat" type="submit">登陸</button> </form> <br> </div> </div> </body> </html>

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>index</h1> <p><a href="{% url 'account_logout' %}">注銷</a></p> <p><a href="{% url 'home' %}">home頁面</a></p> <p><a href="{% url 'change_passwd' %}">修改密碼</a></p> <div> <h2>展示用戶額外信息</h2> <p>{{ request.user }}</p> <p>{{ request.user.userprofile.zhname }}</p> </div> </body> </html>

<h1>更改密碼</h1> {% block main_content %} <div class="col-md-6"> <div class="box box-info"> <div class="box-header with-border"> <h3 class="box-title">{{ request.user.username }}修改密碼</h3> </div> <form class="form-horizontal" action="" method="post"> {% csrf_token %} <div class="box-body"> <div class="form-group"> <label for="id_old_password" class="col-sm-3 control-label">舊密碼:</label> <div class="col-sm-9"> {{ form.old_password.errors }} {{ form.old_password }} </div> </div> <div class="form-group"> <label for="id_new_password1" class="col-sm-3 control-label">新密碼:</label> <div class="col-sm-9"> {{ form.new_password1.errors }} {{ form.new_password1 }} </div> </div> <div class="form-group"> <label for="id_new_password2" class="col-sm-3 control-label">新密碼確認:</label> <div class="col-sm-9"> {{ form.new_password2.errors }} {{ form.new_password2 }} </div> </div> </div> <!-- /.box-body --> <div class="box-footer"> <button type="submit" class="btn btn-default">提交</button> <button type="reset" class="btn btn-info pull-right">重置</button> </div> <!-- /.box-footer --> </form> </div> </div> {% endblock %}

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User,verbose_name='用戶名')
zhname = models.CharField(max_length=50,verbose_name='姓名',blank=True,null=True)
1、頁面效果
2、說明
1. 我們只需要在settings中配置使用ldap認證后,django就可以利用ldap進行認證了
2、然后我們利用django內置模塊 LoginView 定義登錄界面視圖函數 Login(LoginView) 來驗證ldap身份
3、需要身份認證的視圖函數僅需使用django的 login_required 模塊進行裝飾即可:@login_required(login_url='/login')