django的用户认证有很多方法,可以自己写,也有很多第三方的模块。本文记录的是使用Django自带的认证模块的方法,不需要任何其他第三方的包。
1、基本配置
新建好project后,检查settings.py,确保INSTALLED_APPS和MIDDLEWARE中,包含以下模块
INSTALLED_APPS = [
# 其它应用列表...
'django.contrib.auth',
'django.contrib.contenttypes',
]
MIDDLEWARE = [
# 其它中间列表...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
2、添加必要文件
新建一个app,取名为users,并在settings.py的INSTALLED_APPS中加入'users'
新建文件夹templates,在settings.py中修改指定templates的路径:
TEMPLATES = [
{
'DIRS': [os.path.join(BASE_DIR, 'templates')],
#其他配置
},
]
在users目录和templates目录中加入相关的模型和模板文件,最后整个工程的结构如下:
下面介绍各个文件的作用和具体内容:
(1)users/models.py
自定义的用户模型,继承AbstractUser,获得username、password、email、first_name、last_name等属性,可以自己添加相关的属性。
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
nick_name = models.CharField(max_length=50, blank=True)
class Meta(AbstractUser.Meta):
pass
此外,还需在settings.py中设置认证的用户模型,并对语言和时区进行修改。
LANGUAGE_CODE = 'zh-hans' #修改
TIME_ZONE = 'Asia/Shanghai' #修改
AUTH_USER_MODEL = 'users.User' #添加
修改好之后,执行python manage.py makemigrations 和 python manage.py migrate命令,更新数据库
(2)注册部分:users/forms.py和register.html
Django 用户系统内置了登录、修改密码、找回密码等视图,但是唯独用户注册的视图函数没有提供,这一部分需要我们自己来写。
首先,新建注册需要显示的表单模型:users/forms.py
继承自UserCreationForm,但需对用户模型、显示的field内容进行修改。(密码和密码确认不用修改,会自动显示)
#coding: utf-8
from django.contrib.auth.forms import UserCreationForm
from .models import User
class RegisterForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User #表单对应的模型
fields =("username", "email") #需要渲染的控件。默认有用户名、密码、密码确认,此处增加email
其次,新建注册页面的html模板templates/registration/register.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
<title>注册</title>
<style>
.errorlist{
color: red;
}
</style>
</head>
<body>
<div class="flex-center">
<div class="container">
<div class="flex-center">
<div class="unit-1-2 unit-1-on-mobile">
<h3 class="flex-center">注册</h3>
<form class="form" action="{% url 'users:register' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">注册</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
在users/views.py中,新建注册对应的视图函数,并把上面的RegisterForm模型传给html模板
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = RegisterForm()
return render(request, 'registration/register.html', context={'form': form})
最后,在urls.py中添加注册的url:
learn_auth/urls.py:
from django.conf.urls import url, include
from django.contrib import admin
from users import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/', include('users.urls')),
url(r'^users/', include('django.contrib.auth.urls')),
url(r'^$', views.index, name='index'),
]
users/urls.py:
from django.conf.urls import url
from . import views
app_name = 'users'
urlpatterns = [
url(r'^register/', views.register, name='register')
]
至此,启动服务器,访问localhost:8000/users/register即可进行注册了,并会自动带有错误提示功能。效果如下图(注册完成后,跳转至"/",但该功能未实现,会报错,但实际上已经注册成功,可以打开admin页面查看用户信息是否已经加入数据库)
(3)登录部分:login.html
Django 自带有登录的视图函数,url模式在django.contrib.auth.urls中,在工程的urls.py中添加该url即可访问到
url(r'^users/', include('django.contrib.auth.urls'))
自带的视图函数会自动检查数据库,给出登录结果,我们要做的就是提供一个模板文件,用以输入信息和显示登录结果。
登录模块默认调用templates/registration/login.html模块,因此,需要在registration目录下新建login.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>登录</title>
<link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
<style>
.errorlist {
color: red;
}
</style>
</head>
<body>
<div class="flex-center">
<div class="container">
<div class="flex-center">
<div class="unit-1-2 unit-1-on-mobile">
<h3>登录</h3>
<form class="form" action="{% url 'login' %}" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">登录</button>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
<div class="flex-left top-gap text-small">
<div class="unit-2-3"><span>没有账号?<a href="{% url 'users:register' %}">立即注册</a></span></div>
<div class="unit-1-3 flex-right"><span><a href="{% url 'password_reset' %}">忘记密码?</a></span></div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
新建index.html,在其中判断用户是否已登录,若未登录则给出登录连接
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>首页</title>
<link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
</head>
<body>
<div class="flex-center">
<div class="container">
<div>
<h1 class="logo"><a href="{% url 'index' %}">Django Auth Example</a></h1>
{% if user.is_authenticated %}
<p>你已登录,欢迎你:<a href="#">{{ user.username }}</a></p>
<button class="btn btn-default"><a href="{% url 'logout' %}?next={{ request.path }}">注销登录</a></button>
<button class="btn btn-default"><a href="{% url 'password_change' %}?next={{ request.path }}">修改密码</a></button>
{% else %}
<p>你还没有登录,请
<button class="btn btn-default"><a href="{% url 'login' %}?next={{ request.path }}">登录</a></button>
或者
<button class="btn btn-default"><a href="{% url 'users:register' %}?next={{ request.path }}">注册</a></button>
</p>
{% endif %}
</div>
</div>
</div>
</body>
</html>
(4)注销部分:注销不需要模板,直接调用{% url 'users:register' %}即可。
(5)修改密码部分:password_change_form.html和password_change_done.html
当用户需要修改密码时,调用的自带视图函数地址为{% url 'password_change' %}?next={{ request.path }}
需要做的,是添加修改页面和修改成功页面两个html模板password_change_form.html和password_change_done.html。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>修改密码</title>
<link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
<style>
.errorlist {
color: red;
}
</style>
</head>
<body>
<div class="flex-center">
<div class="container">
<div class="flex-center">
<div class="unit-1-2 unit-1-on-mobile">
<h1><a href="{% url 'index' %}">Django Auth Example</a></h1>
<h3>修改密码</h3>
<form class="form" action="{% url 'password_change' %}" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">确认修改</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>密码修改成功</title>
<link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
</head>
<body>
<div class="flex-center">
<div class="container">
<div>
<h1 class="logo"><a href="{% url 'index' %}">Django Auth Example</a></h1>
<p>密码修改成功!</p>
</div>
</div>
</div>
</body>
</html>
(6)重置密码:password_reset_form.html、password_reset_confirm.html、 password_reset_done.html、 password_reset_complete.html
一般重置密码流程为:用户点击“忘记密码”,页面跳转到reset_form.html,输入邮箱和相关信息,点击提交,页面跳转到reset_done页面。后台发送重置邮件给用户,并提供重置密码的页面地址,用户打开reset_confirm页面,设置新的密码并提交,页面跳转到reset_complete页面。
django自带发邮件的功能,只需要在settings.py中进行相关配置,如下面的配置是把邮件发送给终端
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
调用自带重置密码的url:{% url 'password_reset' %}
reset_form和reset_conform的主要部分的内容如下(reset_done和reset_complete只是简单的静态提示内容)
password_reset_form.html :
<form class="form" action="{% url 'password_reset' %}" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">提交</button>
</form>
password_reset_confirm.html
<form class="form" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">提交</button>
</form>
(7)关于注册、登录、注销后的跳转问题
注册、登录、注销等操作后,一般希望页面跳回到原先访问的地方,而不是跳到其他页面。解决方法是在url中添加一个next参数:
<p>你还没有登录,请
<button class="btn btn-default"><a href="{% url 'login' %}?next={{ request.path }}">登录</a></button>
或者
<button class="btn btn-default"><a href="{% url 'users:register' %}?next={{ request.path }}">注册</a></button>
</p>
其中,在login时,因为调用视图函数后,会先访问login.html,并把next参数带过去,而后提交表单给视图函数,此时是一次新的调用,需要把next参数再带回去。方法是在表单后面添加一个hidden的输入
<form class="form" action="{% url 'login' %}" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">登录</button>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
其次,对于用户直接在地址栏输入登录等页面地址的情况,在settings.py中设置默认跳转到首页
LOGOUT_REDIRECT_URL = '/' #没有next参数时的默认注销后跳转地址
LOGIN_REDIRECT_URL = '/' #没有next参数时的默认登录后跳转地址
3、扩展
自带的模块只能通过用户名和密码来认证,如果想要登录时可以通过邮件等其他方式来认证,需要自定义认证模块。
在users目录下新建backends.py,实现authenticate和get_user方法
from .models import User
class EmailBackend(object):
def authenticate(self, request, **credentials):
# 要注意登录表单中用户输入的用户名或者邮箱的 field 名均为 username
email = credentials.get('email', credentials.get('username'))
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
pass
else:
if user.check_password(credentials["password"]):
return user
def get_user(self, user_id):
"""
该方法是必须的
"""
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
配置settings.py:
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'users.backends.EmailBackend',
)