目錄
- #. auth模塊
- 1. 認證 authenticate()
- 2. 登陸 login(HttpRequest, user)
- 3. 注銷 logout(request)
- 4. 認證判斷 is_authenticated()
- 5. 登陸校驗 login_requierd()
- 6. 創建普通用戶 create_user()
- 7. 創建超級用戶 create_superuser()
- 8. 密碼校驗 check_password(password)
- 9. 修改密碼 set_password(new_password)
- User對象的屬性
- 擴展默認的auth_user表
- auth實現
@
我們在開發一個網站的時候,無可避免的需要設計實現網站的用戶系統。此時我們需要實現包括用戶注冊、用戶登錄、用戶認證、注銷、修改密碼等功能,這還真是個麻煩的事情呢。
Django作為一個完美主義者的終極框架,當然也會想到用戶的這些痛點。它內置了強大的用戶認證系統--auth,它默認使用 auth_user 表來存儲用戶數據。
補充:Django內置的生成當前時間的方法
from django.utils.timezone import now now_time = now() # 生成當前時間 # 時間格式示例:2018-11-22 05:17:09.538525+00:00 # 它生成的時間格式能夠與數據庫中的auto_now/auto_now_add生成的時間做計算.
#. auth模塊
# 導入auth模塊
from django.contrib import auth
1. 認證 authenticate()
提供了用戶認證功能,即驗證用戶名以及密碼是否正確,一般需要username 、password兩個關鍵字參數。
如果認證成功(用戶名和密碼正確有效),便會返回一個 User 對象。
authenticate()會在該 User 對象上設置一個屬性來標識后端已經認證了該用戶,且該信息在后續的登錄過程中是需要的。
用法:
# user = auth.authenticate(request, **form_obj.cleaned_data)
user = auth.authenticate(request, username="用戶名", password="密碼")
2. 登陸 login(HttpRequest, user)
該函數接受一個HttpRequest對象,以及一個經過認證的User對象。
該函數實現一個用戶登錄的功能。它本質上會在后端為該用戶生成相關session數據。
session數據表名為:django_session,一個瀏覽器對應一條記錄。
示例:
***
3. 注銷 logout(request)
該函數接受一個HttpRequest對象,無返回值。
當調用該函數時,當前請求的session信息會全部清除。該用戶即使沒有登錄,使用該函數也不會報錯。
示例:
***
4. 認證判斷 is_authenticated()
用來判斷當前請求是否通過了認證(用戶是否登陸)。
示例:
***
5. 登陸校驗 login_requierd()
auth 給我們提供的一個裝飾器工具,用來快捷的給某個視圖添加登錄校驗。
若用戶沒有登錄,則會跳轉到django默認的登錄URL '/accounts/login/ ' ,并傳遞當前訪問url的絕對路徑 (登陸成功后,會重定向到該路徑)。
==如果需要自定義登錄的URL,則需要在settings.py文件中通過LOGIN_URL進行修改,如下:==
# 修改默認的登陸URL為'/login/':
LOGIN_URL = '/login/'
用法:
# 用于驗證是否登陸的裝飾器
from django.contrib.auth.decorators import login_required @login_required
def home(request):pass
6. 創建普通用戶 create_user()
auth 提供的一個創建新用戶的方法,需要提供必要參數(username、password)等。
示例:
***
7. 創建超級用戶 create_superuser()
auth 提供的一個創建新的超級用戶的方法,需要提供必要參數(email, username, password)。
示例:
***
8. 密碼校驗 check_password(password)
auth 提供的一個檢查密碼是否正確的方法,需要提供當前請求用戶的密碼。
密碼正確返回True,否則返回False。
示例:
***
9. 修改密碼 set_password(new_password)
auth 提供的一個修改密碼的方法,接收 要設置的新密碼 作為參數。
==注意:設置完一定要調用用戶對象的save方法!!!==
示例:
User對象的屬性
重要屬性:
- username:用戶名
- password:密文密碼
- is_staff:用戶是否擁有網站的管理權限(是否可登陸admin后臺).
- is_active:是否允許用戶登陸,設置為False后,可以在不刪除用戶的前提下禁止用戶登陸.
對禁用的用戶調用auth.authenticate()方法將返回None
==request.user.xx :返回xx字段的值(xx可以為auth_user表中的所有字段名).
request.user :默認返回username字段的值.==
擴展默認的auth_user表
這內置的認證系統這么好用,但是auth_user表字段都是固定的那幾個,我在項目中沒法拿來直接使用啊!
比如,我想要加一個存儲用戶手機號的字段,怎么辦?
聰明的你可能會想到新建另外一張表然后通過一對一和內置的auth_user表關聯,這樣雖然能滿足要求但是有沒有更好的實現方式呢?
答案是當然有了。
我們可以通過繼承內置的 AbstractUser 類,來定義一個自己的Model類。
這樣既能根據項目需求靈活的設計用戶表,又能使用Django強大的認證系統了。
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):"""用戶信息表"""nid = models.AutoField(primary_key=True)phone = models.CharField(max_length=11, null=True, unique=True)def __str__(self):return self.username
注意:
按上面的方式擴展了內置的auth_user表之后,一定要在settings.py中告訴Django,我現在使用我新定義的UserInfo表來做用戶認證。寫法如下:
# 引用Django自帶的User表,繼承使用時需要設置
AUTH_USER_MODEL = app名.UserInfo'
再次注意:
==一旦我們指定了新的認證系統所使用的表,我們就需要重新在數據庫中創建該表,而不能繼續使用原來默認的auth_user表了。==
auth實現
實現功能:注冊用戶、登陸、注銷、修改密碼、登陸校驗
==settings.py文件增加配置項:==
# 這里配置項目登陸頁面的路由
LOGIN_URL = '/login/'
==urls.py文件:==
from django.conf.urls import url
from django.contrib import admin
from blog import viewsurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^register/$', views.register),url(r'^login/$', views.login),url(r'^index01/$', views.index01),url(r'^index02/$', views.index02),url(r'^logout/$', views.logout),url(r'^change_password/$', views.change_password),url(r'^ajax_register/$', views.ajax_register),
]
==forms.py文件:==
from django import forms
from django.forms import Form
from django.forms import widgets
from django.core.exceptions import ValidationError # 用于拋出錯誤信息# 用戶登陸驗證
class LoginForm(Form):username = forms.CharField(label="用戶名",min_length=2,max_length=6,error_messages={'required': "用戶名不能為空",'invalid': "用戶名格式錯誤",'min_length': "用戶名最短2位",}, # 自定義錯誤提示)password = forms.CharField(label="密碼",min_length=6,max_length=12,error_messages={'required': "密碼不能為空",'invalid': "密碼格式錯誤",'min_length': "密碼最短6位",},widget=widgets.PasswordInput(), # 指定input框的type類型password)# 注冊驗證
class RegForm(Form):username = forms.CharField(label="用戶名",min_length=2,max_length=6,error_messages={'required': "用戶名不能為空",'invalid': "用戶名格式錯誤",'min_length': "用戶名最短2位",}, # 自定義錯誤提示)password = forms.CharField(label="密碼",min_length=6,max_length=12,error_messages={'required': "密碼不能為空",'invalid': "密碼格式錯誤",'min_length': "密碼最短6位",},widget=widgets.PasswordInput(),)re_password = forms.CharField(label="確認密碼",min_length=6,max_length=12,error_messages={'required': "驗證密碼不能為空",'invalid': "密碼格式錯誤",'min_length': "密碼最短6位",},widget=widgets.PasswordInput(),)def clean_re_password(self, *args, **kwargs):password = self.cleaned_data.get('password')re_password = self.cleaned_data.get('re_password')if password == re_password:return passwordraise ValidationError("密碼不一致")# 單選按鈕level = forms.fields.ChoiceField(label="用戶級別",choices=((0, "普通用戶"), (1, "超級用戶"),),initial=0, # 默認選擇普通用戶widget=forms.widgets.RadioSelect(),)# 修改密碼
class ChangePwd(Form):old_password = forms.CharField(label="舊密碼",min_length=6,max_length=12,error_messages={'required': "舊密碼不能為空",'invalid': "密碼格式錯誤",'min_length': "密碼最短6位",},widget=widgets.PasswordInput(),)password = forms.CharField(label="新密碼",min_length=6,max_length=12,error_messages={'required': "新密碼不能為空",'invalid': "密碼格式錯誤",'min_length': "密碼最短6位",},widget=widgets.PasswordInput(),)re_password = forms.CharField(label="確認新密碼",min_length=6,max_length=12,error_messages={'required': "驗證密碼不能為空",'invalid': "密碼格式錯誤",'min_length': "密碼最短6位",},widget=widgets.PasswordInput(),)def clean_re_password(self, *args, **kwargs):password = self.cleaned_data.get('password')re_password = self.cleaned_data.get('re_password')if password == re_password:return passwordraise ValidationError("密碼不一致")
==views.py文件:==
from django.shortcuts import render, redirect, HttpResponse
from blog.forms import * # 導入自定義的Form組件
from django.contrib import auth # 導入認證模塊
from django.contrib.auth.models import User # 導入User表
from django.contrib.auth.decorators import login_required # 用于驗證是否登陸的裝飾器
from blog072 import settings# 注冊
def register(request):form_obj = RegForm()if request.method == 'POST':form_obj = RegForm(request.POST)if form_obj.is_valid():form_obj.cleaned_data.pop('re_password')level = form_obj.cleaned_data.pop('level')# 判斷要注冊的用戶類型if not int(level):# 使用create()方法創建的用戶為明文密碼,無法登陸(不可使用此方法)# User.objects.create(**form_obj.cleaned_data)# create_user():創建普通用戶User.objects.create_user(is_staff=1, **form_obj.cleaned_data)# is_staff=1:允許登陸后臺(默認不允許)else:# create_superuser():創建超級用戶User.objects.create_superuser(email='', **form_obj.cleaned_data)return redirect('/login/')return render(request, 'register.html', {'form': form_obj})# 登陸
def login(request):form_obj = LoginForm()if request.method == 'POST':form_obj = LoginForm(request.POST)if form_obj.is_valid():# 只有執行了is_valid之后,才可執行cleaned_data方法,且cleaned_data內的數據是經過校驗的user = auth.authenticate(request, **form_obj.cleaned_data)# 如果校驗成功,user是當前登陸用戶對象,否則為None(用戶被禁用[is_active=0]后,也會返回None)if user:# 生成session數據(表名:django_session 一個瀏覽器對應一條session數據)auth.login(request, user)# 如果是跳轉過來的,則登陸成功后返回至原頁面:next = request.GET.get('next')ret = next if next else '/index01/'return redirect(ret)print(form_obj.cleaned_data)return HttpResponse("用戶名或密碼錯誤")return render(request, 'login.html', {'form': form_obj})@login_required
def index01(request):print("用戶%s進入index01頁面" % request.user)# request.user.xx :xx可以為auth_user表中的所有字段名# request.user 默認返回username字段return render(request, 'index01.html')def index02(request):if not request.user.is_authenticated():# is_authenticated():判斷當前請求是否通過了認證ret = '%s?next=%s' % (settings.LOGIN_URL, request.path)return redirect(ret)print("用戶%s進入index02頁面" % request.user)return render(request, 'index02.html')# 注銷
@login_required
def logout(request):# 注銷,清除session數據auth.logout(request)return redirect('/login/')# 修改密碼
@login_required
def change_password(request):form_obj = ChangePwd()if request.method == 'POST':form_obj = ChangePwd(request.POST)if form_obj.is_valid():old_password = form_obj.cleaned_data.get('old_password')password = form_obj.cleaned_data.get('password')# 判斷舊密碼是否正確if request.user.check_password(old_password):# 修改密碼request.user.set_password(password)request.user.save() # 同步到數據庫return redirect('/login/')return HttpResponse("舊密碼錯誤!")return render(request, 'change_password.html', {'form': form_obj})# ajax判斷用戶名是否存在
def ajax_register(request):username = request.GET.get('username')print(username)print(User.objects.filter(username=username).exists())if User.objects.filter(username=username).exists():return HttpResponse("0")return HttpResponse()
==HTML文件:==
==注冊頁面(register.html):==
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>我是注冊頁面</title>
</head>
<body>
<form action="" method="post" novalidate>{% csrf_token %}<p id="sign01">{{ form.username.label }}{{ form.username }}<span style="color: red">{{ form.username.errors.0 }}</span><span id="sign02" style="color: red"></span></p><p>{{ form.password.label }}{{ form.password }}<span style="color: red">{{ form.password.errors.0 }}</span></p><p>{{ form.re_password.label }}{{ form.re_password }}<span style="color: red">{{ form.re_password.errors.0 }}</span></p><p>{{ form.level.label }}{{ form.level }}</p><p><button>注冊</button><span style="font-size: 50%;">注冊成功將返回登陸頁面</span></p>
</form>
{% load static %}
<script src="{% static 'jquery-3.3.1.js' %}"></script>
<script>var $UserName = $('#sign01 input');var $Button = $('button');$UserName.on('input', function () {$.ajax({url: '/ajax_register/',type: 'GET',data: {username: $UserName.val(),csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),},success: function (data) {if (data === '0') {$Button.attr('disabled', 'disabled');$('#sign02').text("用戶名已存在");} else {$Button.removeAttr('disabled', 'disabled');$('#sign02').text('');}},});});
</script>
</body>
</html>
==登陸頁面(login.html):==
{# bootstrap版登陸頁面 #}
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1">{# 需要下載bootstrap樣式文件 #}<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>我是登陸頁面</title>
</head>
<body>
<form class="form-horizontal" action="" method="post" novalidate>{% csrf_token %}<div class="form-group"><label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">用戶名</label><div class="col-sm-10">{{ form.username }}{# has-error:Form組件要想展示紅色錯誤信息,必須要加這個類 #}<div class="has-error"><span class="help-block">{{ form.username.errors.0 }}</span></div></div></div><div class="form-group"><label for="{{ form.password.id_for_label }}" class="col-sm-2 control-label">密碼</label><div class="col-sm-10 has-error">{{ form.password }}<div class="has-error"><span class="help-block">{{ form.password.errors.0 }}</span></div></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="submit" class="btn btn-default">登陸</button><a href="/register/" class="btn btn-sm">注冊用戶</a></div></div>
</form>
</body>
</html>
==index01.py頁面:==
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>index01</title>
</head>
<body>
<h1>我是 Index01 頁面</h1>
<a href="/logout/">注銷</a>
<a href="/change_password/">修改密碼</a>
</body>
</html>
==index02.py頁面:==
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>index02</title>
</head>
<body>
<h1>我是 Index02 頁面</h1>
<a href="/logout/">注銷</a>
<a href="/change_password/">修改密碼</a>
</body>
</html>
==修改密碼頁面(change_password.html):==
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>我是修改密碼頁面</title>
</head>
<body>
<form action="" method="post" novalidate>{% csrf_token %}<p>{{ form.old_password.label }}{{ form.old_password }}<span style="color: red">{{ form.odl_password.errors.0 }}</span></p><p>{{ form.password.label }}{{ form.password }}<span style="color: red">{{ form.password.errors.0 }}</span></p><p>{{ form.re_password.label }}{{ form.re_password }}<span style="color: red">{{ form.re_password.errors.0 }}</span></p><p><button>提交</button><span style="font-size: 50%;">修改成功后將返回登陸頁面</span></p>
</form>
</body>
</html>