目录: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')