用户登录验证问题及auth模块的引入
一、自己实现登录验证
二、Django自带的用户验证模块——auth
三、自己动手解决auth不足的地方
一、自己实现登录验证
需求:一共有index和login两个页面,如果用户没有登录访问index页面,则会自动跳转到login页面进行登录,用户在login登录之后,会跳转到index页面,页面出现欢迎用户字样
分析:数据库和session相结合,可以实现不同用户显示不同欢迎字样。不过自己写装饰器太多麻烦,而且如果用户多的话session的验证重复步骤就比较多
1.写入函数
#views.py
from functools import wraps
def check_login(f):
@wraps(f) #取消装饰器装饰完成之后,函数名称改变的问题
def inner(request, *args, **kwargs):
if request.session.get("is_login") == "1": # 如果session中(is_login)对应的value为1,就执行f()函数,否则,返回登录页面
return f(request, *args, **kwargs)
else:
return redirect("/login/")
return inner
def login(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password") #从html中拿到用户输入的账号和密码
user = models.User.objects.filter(username=username, password=password) # 从数据库中拿到账号和密码和用户输入一致的数据
if user: #如果有,则证明用户账号密码正确,如果没有,就返回登录页面
# 登陆成功
request.session["is_login"] = "1" #为这次登录设置一个session,key为is_login,value为'1'
# request.session["username"] = username
request.session["user_id"] = user[0].id # 为这次登录设置一个session,key为username,value为数据库中本账号密码保存的id
# 写上面的一条代码,django后台自动做的事情
# 1. 生成特殊的字符串
# 2. 特殊字符串当成key,在数据库的session表中对应一个session value
# 3. 在响应中向浏览器写了一个Cookie Cookie的值就是 特殊的字符串
return redirect("/index/")
return render(request, "login.html")
@check_login #装饰器,检测用户是否登录,如果登录,就执行index函数,如果没有,就跳转到登录页面
def index(request):
user_id = request.session.get("user_id")
# 根据id去数据库中查找用户
user_obj = models.User.objects.filter(id=user_id) # 设置session的时候,把数据库中的id设为了session中的key,目的就是在这里再次得到id,从数据库中拿出其它数据
if user_obj:
return render(request, "index.html", {"user": user_obj[0]})
else:
return render(request, "index.html", {"user": "匿名用户"})
2.补充index.html页面
# index.html
<!DOCTYPE html>
<html>
<head>
<title>index</title>
</head>
<body>
<h3>This is a test page!</h3>
hello,{{ user.username }}! # 替换登录的名字
</body>
</html>
3.创建数据表
# models.py
class User(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=20, null=False)
password = models.CharField(max_length=20, null=False)
二、Django自带的用户验证模块——auth
在使用这个被前辈们封装好的模块之前,我们还是先来学习一下模块的基本使用方法吧!
1.创建超级用户
python manage.py createsuperuser
# 超级用户数据表(auth_user)是django自动帮忙创建的,但是不可以直接往里面写入数据哦!
# 因为直接写入的话,密码是明文显示的,而一般来说,密码需要以加密形式保存到数据库中
# 如:username='username', password='$MqFtX/a3inUsPdJekYDMh8H4ZkohfCl3Lc4Vj5jZuNI='
2.authenticate()
from django.contrib import auth #导入auth模块
# 验证用户名和密码,如果验证成功,得到的是一个用户对象,如果验证失败,得到的是匿名用户,取它的任意字段都是空
auth.authenticate(username='theuser',password='thepassword')
如果认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性来标识后端已经认证了该用户,且该信息在后续的登录过程中是需要的。
3.login(HttpRequest, user)
auth.login(request, user)
# 将验证的用户注入request.user属性
# 该函数接受一个HttpRequest对象,以及一个认证了的User对象
# 此函数使用django的session框架给某个已认证的用户附加上session id等信息。
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
4.logout(request) 注销用户
# 该函数接受一个HttpRequest对象,无返回值。
# 当调用该函数时,当前请求的session信息会全部清除。
# 该用户即使没有登录,使用该函数也不会报错。
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
5.user对象的 is_authenticated()
要求:
1 用户登陆后才能访问某些页面,
2 如果用户没有登录就访问该页面的话直接跳到登录页面
3 用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址
方法1:
def my_view(request):
if not request.user.is_authenticated():
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
方法2:
from django.contrib.auth.decorators import login_required
@login_required # django已经为我们设计好了一个用于此种情况的装饰器:login_requierd()
def my_view(request):
...
# 若用户没有登录,则会跳转到django默认的登录URL '/accounts/login/ '
# 默认url可以在settings.py文件中通过LOGIN_URL进行修改(在settings.py中加入 LOGIN_URL = '/login/')
# 登陆成功后,会重定向到该路径
注意:
1. 如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。通过认证并不意味着用户拥有任何权限,这个方法甚至也不检查该用户是否处于激活状态,只是表明用户成功的通过了认证。
2. 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name
6.使用create_user辅助函数创建用户
def register(request):
from django.contrib.auth.models import User
user = User.objects.create_user(username='abcd', password='999999999') # 这里把它写死了,实际中要从页面中post过来,得到用户输入的账号和密码
# user = User.objects.create_user(username='',password='',email='') # email可以不写,password至少8个字符,password用哈希算法保存到数据库
# 这里创建用户一共有三种:1. create() 密码明文保存; 2. create_superuser() 创建超级用户 3. create_user() 创建普通用户
return HttpResponse('successful!')
7.使用check_password(passwd)检查密码
def register(request):
from django.contrib.auth.models import User
user_obj = User.objects.create_user(username='abcd', password='999999999')
user_obj.check_password('888888888') # --->返回false
user_obj.check_password('999999999') # --->返回true,为了演示,这里同样也把数值给写死了
8.使用 set_password() 来修改密码
user = User.objects.get(username='') # 得到要修改密码的对象
user.set_password(password='') # 重新设置密码
user.save() # 保存
三、auth_user字段不够的解决方案
1.一对一表
from django.contrib.auth.models import User
class UserDetail(models.Model):
phone = models.CharField(max_length=11)
user = models.OneToOneField(to=User) #这里关联的User就是上面导入的User
2.类的继承(继承auth_user表)
from django.contrib.auth.models import User, AbstractUser
class UserInfo(AbstractUser):
phone = models.CharField(max_length=11)
# 如果使用继承的方式,需要在settings.py中配置 默认用户认证时使用的是哪张表
# AUTH_USER_MODEL = 'app01.UserInfo'
# 另外使用这种方法之前,要把auth自己创建的user表删除才可以
# 作用:替代auth_user并扩展功能
四、未处理的两点内容
is_staff : 用户是否拥有网站的管理权限.
is_active : 是否允许用户登录, 设置为``False``,可以不用删除用户来禁止 用户登录
简单示例
def sign_up(request):
state = None
if request.method == 'POST':
password = request.POST.get('password', '')
repeat_password = request.POST.get('repeat_password', '')
email=request.POST.get('email', '')
username = request.POST.get('username', '')
if User.objects.filter(username=username):
state = 'user_exist'
else:
new_user = User.objects.create_user(username=username, password=password,email=email)
new_user.save()
return redirect('/book/')
content = {
'state': state,
'user': None,
}
return render(request, 'sign_up.html', content)
注册示例代码
@login_required
def set_password(request):
user = request.user
state = None
if request.method == 'POST':
old_password = request.POST.get('old_password', '')
new_password = request.POST.get('new_password', '')
repeat_password = request.POST.get('repeat_password', '')
if user.check_password(old_password):
if not new_password:
state = 'empty'
elif new_password != repeat_password:
state = 'repeat_error'
else:
user.set_password(new_password)
user.save()
return redirect("/log_in/")
else:
state = 'password_error'
content = {
'user': user,
'state': state,
}
return render(request, 'set_password.html', content)
修改密码示例