内置认证系统
Django认证系统处理认证和授权。简而言之,认证验证用户是他们声称是谁,并且授权确定允许验证的用户做什么。这里,术语认证用于指代这两个任务。本文档中只讲解认证部分,授权部分将在下一篇文档中说明。
安装
认证系统打包在 Django 的 django.contrib.auth 应用中。默认情况下,django-admin startproject 生成的 settings.py 中已包含所需的配置,这些配置由 INSTALLED_APPS 设置中列出的两个项目组成:
django.contrib.auth
包含认证框架的核心及其默认模型。django.contrib.contenttypes
是Django 内容类型系统,它允许将权限与您创建的模型相关联。
以及MIDDLEWARE
设置中的以下项目:
SessionMiddleware
跨请求管理 会话;AuthenticationMiddleware
使用会话将用户与请求相关联。
拥有这些配置后,运行命令python manage.py makemigrations
及python manage.py migrate
迁移完成数据库之后,根据配置文件settings.py中的数据库段生成的数据表中已经包含了6张进行认证的数据表,分别是:
- auth_user
- auth_group
- auth_group_permissions
- auth_permission
- auth_user_groups
- auth_user_user_permissions
进行用户认证的数据表为auth_user。
User对象
User对象是Django用户认证系统的核心,通常代表与我们的站点交互的人员, 用于启用诸如限制访问、注册用户配置文件、将内容与创作者相关联等操作。
首先我们看看其在Django源代码中的定义,打开django/contrib/auth/models.py
文件,可以看到:
class AbstractUser(AbstractBaseUser, PermissionsMixin):
"""
An abstract base class implementing a fully featured User model with
admin-compliant permissions.
Username and password are required. Other fields are optional.
"""
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
validators=[username_validator],
error_messages={
'unique': _("A user with that username already exists."),
},
)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=150, blank=True)
email = models.EmailField(_('email address'), blank=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
abstract = True
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""
Return the first_name plus the last_name, with a space in between.
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"""Return the short name for the user."""
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
class User(AbstractUser):
"""
Users within the Django authentication system are represented by this
model.
Username, password and email are required. Other fields are optional.
"""
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
User继承自AbstractUser,其主要的属性有:
- username
- password
- first_name
- last_name
- is_staff:判断用户是否拥有网站的管理权限
- is_active:判断是否允许用户登陆,设置为“False”时可以不用删除用户来禁止用户登陆
users创建
创建users最直接的方式就是使用create_user()
帮助器函数:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', '[email protected]', 'johnpassword')
# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.
>>> user.last_name = 'Lennon'
>>> user.save()
创建superusers
使用createsuperuser
命令创建superusers:
python manage.py createsuperuser --username=joe [email protected]
然后,系统会提示你输入密码。输入密码后,该用户就会立即被创建保存在系统中。如果你使用createsuperuser
命令时,没有输入--username
或者--email
选项时,系统会提示你输入该值。
修改密码
Django 不会在用户模型上存储原始 (明文) 密码, 只有一个哈希值。因此, 不要尝试直接操作 user 的密码属性。这就是创建用户时使用帮助器函数的原因。
为了修改一个user的密码,你可以选择如下方式:
使用命令行修改。
python manage.py changepassword *username*
提供一种命令行修改用户密码的方法。它要求你重复输入两次新密码,当两次新密码匹配后,密码修改成功。r如果使用该命令,但并未提供一个用户名,会默认修改当前系统用户的密码。或者使用
set_password()
在代码中修改密码:>>> from django.contrib.auth.models import User >>> u = User.objects.get(username='john') >>> u.set_password('new password') >>> u.save()
用户认证
使用authenticate()
函数进行用户认证,一般需要username和password两个关键字参数。如果通过认证,authenticate()
函数会返回一个User对象,;认证失败,返回None
。
from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
# A backend authenticated the credentials
else:
# No backend authenticated the credentials
Web请求中的身份验证
Django
使用sessions
和middleware
将身份验证系统挂钩到请求对象中。每一个请求都会提供一个request.user
属性来代表当前的user。如果当前用户没有登录,这个属性会被设置成一个AnonymousUser
的实例,否则它将是User
的一个实例。
login
通过login()
函数将一个已验证过的user保存在当前的session中。
login(request, user, backend=None)
login()
函数使用Django的session框架将user的ID保存在了session中。
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return
logout
logout(request)
与login
相对,当调用logout()
函数后,当前请求的session数据全部被清除。这是为了防止其他人使用相同的 Web 浏览器登录并访问以前用户的会话数据。
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
注意,未登陆的用户调用logout
函数不会抛出错误。
访问限制
要求:
- 用户登陆后才能访问某些页面
- 如果用户没有登陆就访问本应登陆才能访问的页面时会直接跳转到登陆页面
- 用户在登陆页面登陆后,又会自动跳转到之前访问的页面
原始方式
最简单、原始的方式的对界面的访问限制是通过验证request.user.is_authenticated
,验证未通过跳转到登陆页面:
from django.conf import settings
from django.shortcuts import redirect
def my_view(request):
if not request.user.is_authenticated:
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
# ...
或者显示错误信息:
from django.shortcuts import render
def my_view(request):
if not request.user.is_authenticated:
return render(request, 'myapp/login_error.html')
# ...
login_required装饰器
login_required(redirect_field_name=’next’, login_url=None)
你可以使用更简便的login_required()
装饰器来完成上面的功能:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
该装饰器会完成如下功能:
- 如果用户未登录,跳转到配置文件中
settings.LOGIN_URL
指定的页面(可以通过重写此配置完成自定义跳转页面),并将当前访问的页面传递到登录界面,从而当完成登录操作后,会重新跳转到此界面。Example:/accounts/login/?next=/polls/3/
. - 如果用户已登录, 请正常执行该视图。视图代码可以自由地假定用户已登录,不用再判断用户是否登录。
默认情况下, 用户在成功进行身份验证时应重定向到的路径存储在名为 "next" 的 字符串参数中。如果您希望为该参数使用不同的名称, login_required()
提供了一个可选的redirect_field_name
参数。
from django.contrib.auth.decorators import login_required
@login_required(redirect_field_name='my_redirect_field')
def my_view(request):
...
前面有提到,如果想自定义跳转的登录页面可以通过修改配置文件中的LOGIN_URL
属性。当然,还有一个方法是直接在装饰器中指定:
from django.contrib.auth.decorators import login_required
@login_required(login_url='/accounts/login/')
def my_view(request):
...