Py006-02-06用户认证组件

用户认证组件

之前是用django的session设置键值对

但是存在个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
假如你有两个账号  
你用a账号在chrome登陆了 xx站
此时session里生成了 session{'00001':{user:'a'}}

此时你在用b账号在chrome里登陆 xx站
此时session里不会进行重新生成
而是更新 session{'00001':{user:'b'}}
这样相当于覆盖

假设 每个账号权限不一样
a是超级管理员
b是普通用户
此时b登陆时可能数据里还存留a的一些权限数据 这样就容易数据混淆

我们的目标

1
2
3
a登陆生成  a的信息
b登陆生成 b的信息
相互独立

auth模块

1
2
# 需要导入这个模块才可以使用
from django.contrib import auth

django.contrib.auth中提供了许多方法,这里主要介绍其中的三个:

  • 1.1 、authenticate()

提供了用户认证,即验证用户名以及密码是否正确,一般需要username password两个关键字参数
如果认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过authenticate()的User对象会报错的!!

1
user = authenticate(username='someone',password='somepassword')
  • 1.2 、login(HttpRequest, user)  

该函数接受一个HttpRequest对象,以及一个认证了的User对象
此函数使用django的session框架给某个已认证的用户附加上session id等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
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.
...
  • 1.3 、logout(request) 注销用户
1
2
3
4
5
from django.contrib.auth import logout

def logout_view(request):
logout(request)
# Redirect to a success page.

该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。

使用auth模块的大前提

1
2
3
4
5
6
7
8
9
10
11
12
13
用户认证组件:
功能:用session记录登陆状态
前提:用户表:django自带的auth_user表

创建超级用户:python3 manage.py createsuperuser

创建普通用户:python3 manage.py createuser

# 根据 is_supperuser区分 1代表超级用户

api

# 之前使用django里的session 使用的是django_session表

step001新建项目不创建表直接进行数据库迁移

用户表不用自己写了使用 django自带的auth_user表

1
2
python3 manage.py makemigrations
python3 manage.py migrate

step002创建用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
python3 manage.py createsuperuser
# 输入用户名
hjx
# 输入邮箱
直接回车
# 输入密码
111
# 再次输入密码
111

# 此时去看表里生成了一条数据
但是这个密码 已经被加密了 不是你输入的111

我们创建两个账号
|username |password
-----------------------
|hjx |111
|aaa |222

step003 初始代码

urls.py

1
2
3
4
5
6
7
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('login/',views.login),
path('index/', views.index),
path('logout/', views.logout)
]

login.html

1
2
3
4
5
6
<form action="" method="post">
{% csrf_token %}
用户名 <input type="text" name="user">
密码 <input type="text" name="pwd">
<input type="submit" value="submit">
</form>

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from django.shortcuts import render,HttpResponse,redirect

# step001 导入模块
from django.contrib import auth

def login(request):

if request.method=='POST':
user = request.POST.get('user')
pwd = request.POST.get('pwd')
'''
step002 调用
user = auth.authenticate(username=用户名,password=密码)
有一个返回值 如果存在就是这个用户对象 反之None
'''

cur_user = auth.authenticate(username=user,password=pwd)

'''
# 注意虽然用户orm是这个类构造的 但是存在表的时候 密码是加密的 所以无法匹配
from django.contrib.auth.models import User
User.objects.filter(username=user,password=pwd)
'''

if cur_user:
auth.login(request,cur_user) # request.user 当前登陆对象
# 此时访问任意请求的时候 都可以访问到这个用户对象
return redirect('/index/')


return render(request,'login.html')

def index(request):
# 用户对象
print('request.user',request.user)
# 用户id
print('request.user', request.user.id)
# 是否是匿名的
print('request.user', request.user.is_anonymous)

if request.user.is_anonymous:
return redirect('/login/')

username = request.user.username
return render(request,'index.html',{"username":username})

def logout(request):
# 注销 实质就是request.session.flush()
auth.logout(request)

return redirect('/login/')

index.html

1
2
3
4
5
6
7
<body>
<h3>hi {{ username }}</h3>
{#也可以在模版里直接使用#}
<h4>hi {{ request.user.username }}</h4>

<a href="/logout/">注销</a>
</body>

User对象

User 对象属性:username, password(必填项)password用哈希算法保存到数据库

2.1 、user对象的 is_authenticated()

如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。

通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name

要求:

  1. 用户登陆后才能访问某些页面,

  2. 如果用户没有登录就访问该页面的话直接跳到登录页面

  3. 用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址

  • 方法1:
1
2
3
def my_view(request):
if not request.user.is_authenticated():
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
  • 方法2:
django已经为我们设计好了一个用于此种情况的装饰器:login_requierd()
注意:未登陆要跳转的默认页码要在settings.py进行设置
1
2
3
4
5
# 这个值本身是没有的
LOGIN_URL = '/login/'

# 默认会跳
accounts/login/?next=/index/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.contrib.auth.decorators import login_required

@login_required
def other(request):
# 不用在做验证登陆的重定向了
# ...


def login(request):

if user:
auth.login(request,user)
# 从失效页面返回到login时 url会变成 login/?next=/other/
next_url = request.GET.get('next','/index/')
return redirect(next_url)

若用户没有登录,则会跳转到django默认的 登录URL ‘/accounts/login/ ‘ (这个值可以在settings文件中通过LOGIN_URL进行修改)。并传递 当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。

1
2
3
4
5
6
7
8
9
10
11
12
# 之前判断是否登陆是
def index(request):
# 通过is_anonymous判断
if request.user.is_anonymous:
return redirect('/login/')

username = request.user.username
return render(request,'index.html',{"username":username})

# 通过 is_authenticated是否登陆
if not request.user.is_authenticated:
return redirect('/login/')

2.2 、创建用户

1
2
3
4
5
6
7
8
9
10
11
from django.contrib.auth.models import User
user = User.objects.create_user(username='',password='')

'''
# 注意不要直接用这个方式
# 注意不要直接用这个方式
# 注意不要直接用这个方式
user = User.objects.create(username='',password='')
因为auth_user里的password是加密处理后的
这种方式创建没有经过加密
'''

注册用户的需求

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def reg(request):
if request.method=='POST':
user = request.POST.get('user')
pwd = request.POST.get('pwd')

'''
# 千万别用
User.objects.create(username=user,password=pwd)
# 因为密码是 加密的
'''
from django.contrib.auth.models import User
user = User.objects.create_user(username=user, password=pwd)

return redirect('/login/')

return render(request,'reg.html')

reg.html

1
2
3
4
5
6
7
<h3>注册</h3>
<form action="" method="post">
{% csrf_token %}
用户名 <input type="text" name="user">
密码 <input type="text" name="pwd">
<input type="submit" value="submit">
</form>

2.3 、check_password(passwd)

用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True

2.4 、修改密码

使用 set_password() 来修改密码

1
2
3
user = User.objects.get(username='')
user.set_password(password='')
user.save

2.5 、简单示例

  • 注册:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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)
  • 修改密码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@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)

总结

  • api常用的
1
2
3
4
5
6
7
8
9
10
11
12
13
from django.contrib import auth
1. if验证成功返回user对象,否则返回None
user=auth.authenticate(username='',password='')

2. auth.login(request,user) # request.user # 当前登陆对象

3. auth.logout(request)

from django.contrib.auth.models import User # User(orm模型) = auth_user表

4. request.user.is_authenticated()

5. user = User.objects.create_user(username='',password='')
  • 补充
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
匿名用户
class models.AnonymousUser

django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:

id 永远为None。
username 永远为空字符串。
get_username() 永远返回空字符串。
is_staff 和 is_superuser 永远为False。
is_active 永远为 False。
groups 和 user_permissions 永远为空。
is_anonymous() 返回True 而不是False。
is_authenticated() 返回False 而不是True。
set_password()、check_password()、save() 和delete() 引发 NotImplementedError。
New in Django 1.8:
新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。
  • 总结
1
2
3
4
5
6
7
8
9
如果没登录
request.user = 匿名用户对象

如果登录了

request.user = 登录对象

request.user 是一个全局变量
可以在任何视图和模版里使用