五、用户的身份验证

用户的身份验证

Django提供了用户验证机制。我们将使用django.contrib.auth包中的auth应用。

设置身份验证

在使用Django提供的身份验证机制之前,要正在项目的setting.pay文件中添加相关的设置。在 settings.py 文件中找到 INSTALLED_APPS 列表,检查有没有列出django.contrib.auth和django.contrib.contenttypes。INSTALLED_APPS 列表应该类似下面这样:

INSTALLED_APPS =[
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rango', 
        ]

django.contrib.auth 用于访问 Django 提供的身份验证系统,django.contrib.contenttypes 供 auth 应用跟踪数据库中的模型。

注:如果 INSTALLED_APPS 列表中没有 django.contrib.auth 和 django.contrib.contenttypes,要 自己添加,添加后还要执行 python manage.py migrate 命令更新数据库,添加所需的表。

密码哈希

Django的auth用用默认存储的是经过PBKDF2算法计算过的馍吗哈希值,可以有效保护用户数据的安全。如果不指定则默认上诉的算法,若指定在setting.py中加如下代码段:

PASSWORD_HASHERS = [
        'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
        'django.contrib.auth.hashers.BCryptPasswordHasher',
        'django.contrib.auth.hashers.PBKDF2PasswordHasher',
        'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
]

以上算法若第一个无效,则会依次使用下面的算法。

密码验证器

在 Django 项目的 settings.py 模块中有个字典构成的列表,名为 AUTH_PASSWORD_VALIDATORS。 在嵌套的字典中可以清楚地看出,Django自带了一些常用的密码验证器,例如针对长度的验证器。每个验证器都有 OPTIONS 字典,以便自定义选项。假如你想确保密码最短为 6 个字符,那 么可以把 MinimumLengthValidator 的 min_length 选项设为 6,如下所示:

    AUTH_PASSWORD_VALIDATORS = [
        ...
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
            'OPTIONS': { 'min_length': 6, }
},
...
]

除了自带的还可以自定义。

User模型

User 对象(django.contrib.auth.models.User)是 Django 身份验证系统的核心,表示与 Django 应用交互的每个个体。

	User模型有5个主要的属性:
	❏ 用户账户的用户名(username) 
	❏ 用户账户的密码(password) 
	❏ 用户的电子邮件地址(email) 
	❏ 用户的名字(first_name)
	❏ 用户的姓(last_name)

此外,User 模型还有其他属性,例如 is_active、is_staff 和 is_superuser。这些属性的值都是 布尔值,分别用于指明账户是否激活、是否为团队成员,以及是否拥有超级用户权限。

增加用户的属性

除了 User 模型提供的属性之外,如果还需要其他用户相关的属性,要自己定义一个与 User 模型关联的模型。为此需要在model.py文件中定义一个模型。如下:

class UserProfile(models.Model): 
	# 这一行是必须的
	# 建立与 User 模型之间的关系
	user = models.OneToOneField(User)
	# 想增加的属性
	website = models.URLField(blank=True)
	picture = models.ImageField(upload_to='profile_images', blank=True)
	# 覆盖 __str__() 方法,返回有意义的字符串
	def __str__(self):
		return self.user.username

注:这个模型与 User 模型之间建立的一对一关系。因为引用了默认的 User 模型,所以要在models.py 文件中导入它:

 from django.contrib.auth.models import User

创建用户注册视图和模板

实现用户注册功能的步骤如下:

	❏ 定义 UserForm 和 UserProfileForm
	❏ 添加一个视图,处理创建新用户的过程
	❏ 创建一个模板,显示 UserForm 和 UserProfileForm
	❏ 把 URL 映射到前面添加的视图上

最后,还要在首页添加一个链接,指向注册页面。

定义 UserForm 和 UserProfileForm

所需要的类

from django import forms
from django.contrib.auth.models import User
from rango.models import Category, Page, UserProfile

我们要在需要登录或注册的应用中定义两个类,继承自 forms.ModelForm。其中一个针对 User 类,另一个针对前面定义的 UserProfile 模型。这两个 ModelForm 子类创建的 HTML 表单用于显示相应模型的字段,为我们节省了很多工作量。

class UserForm(forms.ModelForm):
	password = forms.CharField(widget=forms.PasswordInput())
		class Meta:
		model = User
		fields = ('username', 'email', 'password')


class UserProfileForm(forms.ModelForm):
	class Meta:
	model = UserProfile
	fields = ('website', 'picture')

Meta 类的作用是为所在的类提供额外的属性。Meta类中必须有 model 字段。UserForm 类对应的模型是 User。此外,还要通过 fields 或 exclude 指定要在表单中显示或排除的字段。

注意 UserForm 中定义了 password 属性。虽然 User 模型实例有 password 属性,但是在渲染的 HTML 表单中这个字段的值不会被遮盖,用户输入的密码是可见的。鉴于此,我们重新定义了password 属性,指定使用 PasswordInput() 小组件显示这个 CharField,以防用户输入的密码被人
窥见。

定义注册( register())或登录(login())的 视图

接下来要渲染表单及处理表单数据

注册视图验证:
在相应的应用 views.py 模块中,添加一个 import 语句,导入新定义的 UserForm 和 UserProfileForm 类。

from rango.forms import UserForm, UserProfileForm
def register(request):
# 一个布尔值,告诉模板注册是否成功
# 一开始设为 False,注册成功后改为 True
	registered = False
	# 如果是 HTTP POST 请求,处理表单数据
	if request.method == 'POST':
		# 尝试获取原始表单数据
		# 注意,UserForm 和 UserProfileForm 中的数据都需要
		user_form = UserForm(data=request.POST)
		profile_form = UserProfileForm(data=request.POST)
		# 如果两个表单中的数据是有效的……
		if user_form.is_valid() and profile_form.is_valid():
			# 把 UserForm 中的数据存入数据库
			user = user_form.save()
			# 使用 set_password 方法计算密码哈希值
			# 然后更新 user 对象
			user.set_password(user.password)
			user.save()
			# 现在处理 UserProfile 实例
			# 因为要自行处理 user 属性,所以设定 commit=False
			# 延迟保存模型,以防出现完整性问题
			profile = profile_form.save(commit=False)
			profile.user = user
			# 用户提供头像了吗?
		# 如果提供了,从表单数据库中提取出来,赋给 UserProfile 模型
			if 'picture' in request.FILES:
				profile.picture = request.FILES['picture']
				# 保存 UserProfile 模型实例
				profile.save()
				# 更新变量的值,告诉模板成功注册了
				registered = True
	else:
		# 表单数据无效,出错了?
		# 在终端打印问题
		print(user_form.errors, profile_form.errors)
		else:
		# 不是 HTTP POST 请求,渲染两个 ModelForm 实例
		# 表单为空,待用户填写
		user_form = UserForm()
		profile_form = UserProfileForm()
	# 根据上下文渲染模板
	return render(request,
	'rango/register.html',
	{'user_form': user_form,
	'profile_form': profile_form,
	'registered': registered})

登录视图验证:

所需要的类、包:

from django.contrib.auth import authenticate, login
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
def user_login(request):
	# 如果是 HTTP POST 请求,尝试提取相关信息
	if request.method == 'POST':
		# 获取用户在登录表单中输入的用户名和密码
		# 我们使用的是 request.POST.get('<variable>')
		# 而不是 request.POST['<variable>']
		# 这是因为对应的值不存在时,前者返回 None,
		# 而后者抛出 KeyError 异常
		username = request.POST.get('username')
		password = request.POST.get('password')
		# 使用 Django 提供的函数检查 username/password 是否有效
		# 如果有效,返回一个 User 对象
		user = authenticate(username=username, password=password)
		# 如果得到了 User 对象,说明用户输入的凭据是对的
		# 如果是 None(Python 表示没有值的方式),说明没找到与凭据匹配的用户
		if user:
		# 账户激活了吗?可能被禁了
			if user.is_active:
			# 登入有效且已激活的账户
			# 然后重定向到首页
				login(request, user)
				return HttpResponseRedirect(reverse('index'))
			else:
	# 账户未激活,禁止登录
				return HttpResponse("Your Rango account is disabled.")
		else:
		# 提供的登录凭据有问题,不能登录
			print("Invalid login details: {0}, {1}".format(username, password))
			return HttpResponse("Invalid login details supplied.")
	# 不是 HTTP POST 请求,显示登录表单
	# 极有可能是 HTTP GET 请求
	else:
	# 没什么上下文变量要传给模板系统
	# 因此传入一个空字典
		return render(request, 'rango/login.html', {})

创建注册页面的模板

注册模板:

创建注册页面的模板
现在要创建 register() 视图的模板。新建 rango/register.html 文件,写入下述代码。
{% extends 'rango/base.html' %}
{% load staticfiles %}

{% block title_block %}
	Register
{% endblock %}

{% block body_block %}
	<h1>Register for Rango</h1>
{% if registered %}
Rango says: <strong>thank you for registering!</strong>
<a href="{% url 'index' %}">Return to the homepage.</a><br />

{% else %}
	Rango says: <strong>register here!</strong><br />
<form id="user_form" method="post" action="{% url 'register' %}" enctype="multipart/form-data">
	{% csrf_token %}
	<!-- 显示每个表单 -->
	{{ user_form.as_p }}
	{{ profile_form.as_p }}
	 <!-- 提供一个按钮,点击后提交表单 -->
	<input type="submit" name="submit" value="Register" />
</form>
{% endif %}
{% endblock %}

我们为 < form> 元素设定了 enctype 属性。这是因为,如果用户想上传头像,表单数据中将包含二进制数据,而且可能相当大。传给服务器时,这些数据要分成几部分。因此,我们要设定 enctype=“multipart/form-data”,让 HTTP 客户端(Web 浏览器)分段打包和发送数据。如若不然,服务器收不到用户提交的全部数据。

{% extends 'rango/base.html' %}
{% load staticfiles %}
{% block title_block %}
		Login
{% endblock %}


{% block body_block %}

<h1>Login to Rango</h1>

<form id="login_form" method="post" action="{% url 'login' %}">
		{% csrf_token %}
		Username: <input type="text" name="username" value="" size="50" />
<br />
		Password: <input type="password" name="password" value="" size="50" />
<br />
		<input type="submit" value="submit" />
</form>
{% endblock %}

添加 URL 映射

有了视图和对应的模板之后,现在可以添加 URL 映射了。打开你需要登录和注册的 应用的 urls.py 模块,根据下述代码修改 urlpatterns。

urlpatterns = [
	url(r'^register/$',views.register,name='register'), # 新增的模式
	url(r'^login/$', views.user_login, name='login'),
]

限制访问

现在用户可以登录 Rango 应用了,根据客户的要求,接下来应该限制访问应用的某些页面,即只有注册的用户才能添加分类和网页。在 Django 中,这一要求有多种实现方式。

❏ 在模板中可以使用 {% if user.authenticated %} 模板标签修改页面渲染的内容
❏ 在视图中可以直接通过 request 对象检查用户有没有通过身份验证
❏ 此外,还可以使用 Django 提供的 @login_required 装饰器检查用户有没有通过身份验证
使用装饰器限制访问

所需要的类:

from django.contrib.auth.decorators import login_required
@login_required
def restricted(request):
return HttpResponse("Since you're logged in, you can see this text!")

注意,装饰器的用法是直接放在函数的签名上方,并在装饰器的名称前放一个 @ 符号。Python 会先执行装饰器,再执行函数(方法)的代码。

在应用中添加映射:

url(r'^restricted/', views.restricted, name='restricted')

还要处理未登录的用户尝试访问 restricted() 视图的情况。此时应该怎么做呢?最简单的处理方法是重定向到他们能访问的页面,例如登录页面。这个页面的地址在项目的 settings.py 模块中设定。打开 settings.py 文件,定义 LOGIN_URL 变量,把值设为未登录时的重定向地址。这里,设为 /rango/login/ URL 上的登录页面:

LOGIN_URL = '/rango/login/'

这样设定后,login_required() 装饰器将把未登录的用户重定向到 /rango/login/ URL。

退出

所需的类:

from django.contrib.auth import logout

为了提供退出功能,打开 rango/views.py 文件,添加名为 user_logout() 的视图,代码如下:

# 使用 login_required() 装饰器限制
# 只有已登录的用户才能访问这个视图
@login_required
def user_logout(request):
	# 可以确定用户已登录,因此直接退出
	logout(request)
	# 把用户带回首页
	return HttpResponseRedirect(reverse('index'))

添加链接:

url(r'^logout/$', views.user_logout, name='logout'),

让未登录的用户显示登录按钮,让登录的用户显示退出按钮:

<ul>
{% if user.is_authenticated %}
<li><a href="{% url 'restricted' %}">Restricted Page</a></li>
<li><a href="{% url 'logout' %}">Logout</a></li>
{% else %}
<li><a href="{% url 'login' %}">Login</a></li>
<li><a href="{% url 'register' %}">Register Here</a></li>
{% endif %}
<li><a href="{% url 'add_category' %}">Add a New Category</a></li>
<li><a href="{% url 'about' %}">About</a></li>
<li><a href="{% url 'index' %}">Index</a></li>
</ul>

自此登录和注册功能以基本实现

之后也可使用Django-Registration-ReduxDjango 项目提供登录、注册、一步和两步身份验证、密码修改、密码重设等功能。对以上可以进行进一步修改观看另一篇博客Django-Registration-ReduxDjango的使用

以上为我在学习过程中从书中整理的笔记,以便以后查看,若有错误请谅解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值