在我们构建一个网站的时候,想要对网站的资源进行访问控制,那么添加身份认证机制就是一个非常必要的办法
只有当访问者进行用户登录认证之后,才能对其开放网页的访问权限,甚至可以对用户进行分级管理,按照用户级别赋予用户不同的权限
简单的登录认证机制
最简单的认证机制只需要校验用户的登录名和密码,验证其是否与数据库中存储的用户数据一致,如果一致,则认证通过
以下是具体操作过程
构建用户模型
我们的用户数据是要存储在数据库中的,那么必须要构造一个用户模型来存储用户的数据
最简单的模型,只有用户名和密码
# models.py
from django.db import models
class User(models.Model):
username = models.CharField(verbose_name='用户名', max_length=40, unique=True)
password = models.CharField(verbose_name='口令', max_length=256)
def __str__(self):
return self.username
在命令行中执行以下命令,使构建的用户模型生效
python manage.py makemigrations # 更新模型
python manage.py migrate # 在数据库中创建表结构
模板设计
首先设计网页模板,这是用户与服务器进行交互的接口,也是服务器数据在用户端的展示窗口
登录界面
<!-- login.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>登录</title>
</head>
<body>
<div class="login">
<h2>欢迎登录!</h2>
<br/>
<form action="/login/" method="post">
{% csrf_token %}
<p>账号:
<input type="text" name="username" value="">
</p>
<p>密码:
<input type="password" name="password">
</p>
<br/>
<input type="submit" value="登录">
<button type="button" onclick="window.location.href='/register/'">注册</button>
</form>
<div>{{ mess }}</div>
</div>
</body>
</html>
使用命令 startproject 创建的 Django 项目默认使用 CSRF 防护中间件 django.middleware.csrf.CsrfViewMiddleware,所以在每个表单中都需要加上 {% csrf_token %} 标签,否则 CSRF 校验不通过,触发 403 错误
可以在 setting 文件中修改列表 MIDDLEWARE 的值来关闭这个中间件,但不建议
注册界面
<!-- register.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>用户注册</title>
</head>
<body>
<div>
<h2>请输入待注册的用户信息</h2>
<form action="/register/" method="post">
{% csrf_token %}
<p>账号:
<input type="text" name="username">
</p>
<p>密码:
<input type="password" name="password">
</p>
<p>再次输入:
<input type="password" name="re_password">
</p>
<br/>
<input type="submit" value="注册用户">
</form>
</div>
<a href="/login/">返回登录</a>
<div>{{ mess }}</div>
</body>
</html>
这里就比登录的多了个口令确认输入,相当地简单
视图函数
视图函数是对用户请求的处理,这里定义对认证机制的一系列请求的处理函数
登录和注销
# views.py
from django.shortcuts import render, redirect
from Blog.models import User, Article
def login(request):
if request.session.get('is_login', None): # 检查是否已登录
return redirect('/index/')
if request.method == 'POST': # 如果有提交表单
username = request.POST.get('username', '').strip()
password = request.POST.get('password', '').strip()
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return render(request, 'login.html', {'mess': '用户不存在'})
else:
if password == user.password:
request.session['is_login'] = 'T' # 标记已登录
request.session['user_id'] = user.id
request.session['username'] = username
return redirect('/index/')
else:
return render(request, 'login.html', {'mess': '密码输入错误'})
return render(request, 'login.html')
def logout(request):
if request.session.get('is_login', None):
request.session.flush()
return redirect('/login/')
最简单的,只需要验证用户名和密码
验证通过,则在 session 中标记已登录和记录当前登录用户的一些信息,然后重定向到网站首页
验证不通过,返回错误提示信息
注销处理,只需要把 session 清空,然后重定向到登录页面
注册
# views.py
from django.shortcuts import render
from Blog.models import User
def register(request):
if request.session.get('is_login', None):
request.session.flush()
if request.method == 'POST':
username = request.POST.get('username', '').strip()
password = request.POST.get('password', '').strip()
re_password = request.POST.get('re_password', '').strip()
if password == re_password:
try:
User.objects.get(username=username)
except User.DoesNotExist:
new_user = User(username=username, password=password)
new_user.save()
return render(request, 'register.html', {'mess': '用户注册成功'})
else:
return render(request, 'register.html', {'mess': '该用户已被注册'})
else:
return render(request, 'register.html', {'mess': '两次密码输入不一致'})
return render(request, 'register.html')
这里先验证了用户密码的两次输入是否一致,避免用户输入错误
如果不一致,直接返回错误提示信息
如果一致,则在数据库中查找用户名,看是否已被注册。如果未被注册,则注册成功,把新用户的数据存入数据库。否则,注册失败,返回错误提示信息
登录检测
对于一些资源请求,我们设置需要登录认证过后才能进行访问
如果在每个请求的处理函数前面都添加一个登录检测,那么工程量比较大(如果很多请求),而且很丑相当不美观
这里我们可以使用 Python 的装饰器,在不需要改动原函数的情况下,扩展函数的功能
# views.py
from django.shortcuts import render, redirect
from Blog.models import Article
from functools import wraps
def check_login(func):
@wraps(func)
def inner(request, *arg, **kwargs):
if request.session.get('is_login', None):
return func(request, *arg, **kwargs)
return redirect('/login/')
return inner
@check_login
def index(request):
arts = Article.objects.defer('body').order_by('modifield_time').reverse()
return render(request, 'index.html', {'username': request.session.get('username'), 'arts': arts})
wraps 是一个可以确保调用装饰器时不会改变原函数属性(如模块名__name__)的东西
也可以使用 Django 内置的登录检测装饰器
# views.py
from django.shortcuts import render
from Blog.models import Article
from django.contrib.auth.decorators import login_required
@login_required
def index(request):
arts = Article.objects.defer('body').order_by('modifield_time').reverse()
return render(request, 'index.html', {'username': request.session.get('username'), 'arts': arts})
这个需要在 settings 文件中设置 LOGIN_URL 的值来指定跳转路径
URL 路由设置
最后把 url 请求和对应的视图函数进行关联
# urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('logout/', views.logout),
path('register/', views.register),
path('index/', views.index),
]
效果展
登录成果后,进入首页
安全机制
从安全的角度上来看,上面实现的那个东西就跟被拔光了毛的老母鸡一样,任人宰割
所以我们需要给它多加点东西,加的东西放在后面几篇