1. cookie 与 session
发展史:
1. 网站不保存用户信息,所有用户的访问返回结果都一样.(eg:新闻,博客)
2. 需要保存用户信息,所有用户的放回都是单独的.(eg:淘宝)
以登入功能为例:
如果不保存用户登入状态,也就意味着用户每次访问网站都需要重复的输入用户名密码.
当用户第一次登入成功之后,将用户的名与密码返回给浏览器,在本地保存,
之后在访问这个网站的时候,浏览器会自动将用户名个密码发给服务器,服务自动校验.
cookie:服务端保存在客户端浏览器上的信息都可以称为cookie.
它的表现形式一帮是k:v键值对(可以有多个)
早期这种方式非常大的安全隐患.
优化:
当用户登入成功之后,服务端产生一个随机字符串,交由客户端浏览器保存,
之后访问服务端的时候,都带着该随机字符串.服务端去数据库中比对是否有对应的随机字符串
从而获取到对应的用户信息.
如果截取到该字符串,那么可以冒充这个用户,还是有安全隐患.
在web领域没有绝对的安全,也没有绝对的不安全.
session:数据是保存在服务端的并且他的表现形式一帮都是k:v键值对(可以有多个)
session 是基于cookie工作的(大部分的保存用户状态的操作都是需要使用到cookie).
ps:
token: session虽然是保存在服务的,但是经不住量大.
服务端不再保存数据,登入之后将一段信息进行加密处理,
将加密之后的密文结果拼在信息后面保存到浏览器,
浏览器下次访问的时候带着该信息,
服务端字段切去前面一段信息在再次使用自己的加密算法跟计算机尾部的密文比对.
jwt认证:(使用多)
三端信息
2. Cookie操作
虽然cookie是服务端告诉浏览器需要保存内容,但是客户端浏览器可以选择拒绝保存.
如果禁止了,只要是需要记录用户状态的网站登陆功能都无法使用.
2.1 生成对象
return HttpResponse()
return render()
return redirec()
HttpResponse_obj = HttpResponse()
return HttpResponse_obj
render_obj = render()
return render_obj
redirec_obj = redirec()
return redirec_obj
2.2 Cookie操作方法
# 设置cookie
obj.set_cookit(key, value)
# 获取 cookie()
request.COOKIES.get(key)
# 超时都是以秒为单位
为cookie设置一个超时时间,5秒到期
obj.set_cookit(key, value, max_age = 5)
# 针对ie浏览器
obj.set_cookit(key, value, expires=)
# 删除cookie
obj.delete_cookie(key)
2.3 登入功能
写一个登入功能.
登入之后进入到主页
1. 路由层
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.login),
url(r'^home/', views.home)
]
2. 视图层
from django.shortcuts import render, redirect, HttpResponse
from django import forms
class MyForms(forms.Form):
username = forms.CharField(
label='名称',
min_length=3, max_length=6,
error_messages={
'min_length': '名称过短',
'max_length': '名称过长',
'required': '名称不能为空'
},
widget=forms.widgets.TextInput(
attrs=({'class': 'form-control'})
)
)
password = forms.CharField(
label='密码',
min_length=3,
max_length=11,
error_messages={
'min_length': '密码过短',
'max_length': '密码过长',
'required': '名称不能为空'
},
widget=forms.widgets.PasswordInput(
attrs=({'class': 'form-control'})
)
)
def login(request):
forms_obj = MyForms()
if request.method == 'POST':
print(request.POST)
if request.POST.get('username') == 'kid' and request.POST.get('password') == '123':
return redirect('/home/')
else:
forms_obj = MyForms(request.POST)
return render(request, 'login.html', locals())
def home(request):
return render(request, 'home.html')
3. 前端页面
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登入功能title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<form action="" method="post" novalidate>
{% for form in forms_obj %}
<p>{{ form.label }}:{{ form }} <span style="color: red;">{{ form.errors.0 }}span>p>
{% endfor %}
<input type="submit">
form>
div>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>主页title>
head>
<body>
<div>
我是主页
div>
body>
html>
0. 浏览器中输入 127.0.0.1/login 登入页面中 输入 名称 密码 进去主页.
1. 浏览器中输入 127.0.0.1/home 直接访问.
4. 设置cookie
在登入验证处设置 cookie 信息
if username == 'kid' and password == '123':
"""
1. 生成对象
2. 设置cookie
3. 返回对象
"""
redirect_obj = redirect('/home/')
redirect_obj.set_cookie('username', 'kid666')
return redirect_obj
def login(request):
forms_obj = MyForms()
if request.method == 'POST':
print(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'kid' and password == '123':
"""
1. 生成对象
2. 设置cookie
3. 返回对象
"""
redirect_obj = redirect('/home/')
redirect_obj.set_cookie('username', 'kid666')
return redirect_obj
else:
forms_obj = MyForms(request.POST)
return render(request, 'login.html', locals())
* 设置cookie后在测试, 测试前对cookie清除.
0. 浏览器中输入 127.0.0.1/login 登入页面中 输入 名称 密码 进去主页.
查看到cookie中有数据.
cookie 中有数据
1. 浏览器中输入 127.0.0.1/home 直接访问.
cookie 是没有数据的. 显示非法访问
5. 获取cookie数据
cookie数据被保存到request中
通过request.COOKIES.get('key') 获取值
如果能获取到数据那说明是通过登入页面正常输入账户密码进入主页的,
不然就直接访问的.
def home(request):
if request.COOKIES.get('username') == 'kid666':
return render(request, 'home.html')
else:
return HttpResponse('非法访问')
0. 浏览器中输入 127.0.0.1/login 登入页面中 输入 名称 密码 进去主页.
cookie 中有数据
1. 浏览器中输入 127.0.0.1/home 直接访问.
cookie 是没有数据的.
6. 检验装饰器
如果有多个需要验证才能进入的页面,就可以制作一个校验信息的登入装饰器.
url(r'^index1', views.),
url(r'^index2', views.index2),
url(r'^index3', views.index2)
def verification(func):
def inner(request, *args, **kwargs):
if request.COOKIES.get('username') == 'kid666':
res = func(request, *args, **kwargs)
return res
else:
return redirect('/login/')
return inner
@verification
def index1(request):
return HttpResponse('需要验证才能登入的网页1')
@verification
def index2(request):
return HttpResponse('需要验证才能登入的网页2')
@verification
def index3(request):
return HttpResponse('需要验证才能登入的网页3')
在没有cookie中没有数据的时候是无法直接进入到
index1 index2 index3 的页面
被重定向到login功能中,要求登入...
输入正确的用户和密码.
只有当cookie有了数据,并且数据符合要求,
就能够直接访问 index1 index2 index3 的页面.
7. 登入返回
现在存在的问题是 cookie中没有数据 进入 127.0.0.1/index1 2 3
跳转到登入页面,成功登入之后,应该返回用户之前想要访问的页面去.
而不是进入了home页面.
request.path
提供相对于根目录的url相对路径,不包含参数。它的输出是一个字符串
/index1
request.path_info
提供相对于根目录的url相对路径,不包含参数。它的输出也是一个字符串,与request.path相同.
/index1
request.get_full_path()
获取包含完整参数的相对于根目录的相对url路径。
/index1/?name=kid
request.build_absolute_uri()
该方法用于获取带域名的url绝对路径, 是一个字符串.
http://127.0.0.1:8000/index1/?name=kid
target_url = request.get_full_path()
return redirect('/login/?next=%s' % target_url)
def verification(func):
def inner(request, *args, **kwargs):
if request.COOKIES.get('username') == 'kid666':
res = func(request, *args, **kwargs)
return res
else:
target_url = request.get_full_path()
return redirect('/login/?next=%s' % target_url)
return inner
当前的form表单的提交方式是post
<!--4. form表单 post请求 前端不验证 -->
<form action="" method="post" novalidate>
def login(request):
forms_obj = MyForms()
if request.method == 'POST':
print(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'kid' and password == '123':
target_url = request.GET.get('next')
print(target_url)
if target_url:
redirect_obj = redirect(target_url)
else:
redirect_obj = redirect('/home/')
redirect_obj.set_cookie('username', 'kid666')
return redirect_obj
else:
forms_obj = MyForms(request.POST)
return render(request, 'login.html', locals())
8. 注销时清除cookie
url(r'^logout/', views.logout)
@verification
def index1(request):
return render(request, 'index1.html')
@verification
def logout(request):
redirect_obj = redirect('/login/')
redirect_obj.delete_cookie('username')
return redirect_obj
<li><a href="/logout/">退出登入</a></li> 绑定logout
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index1title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
<span class="icon-bar">span>
<span class="icon-bar">span>
<span class="icon-bar">span>
button>
<a class="navbar-brand" href="#">Branda>
div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)span>a>li>
<li><a href="#">Linka>li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
<ul class="dropdown-menu">
<li><a href="#">Actiona>li>
<li><a href="#">Another actiona>li>
<li><a href="#">Something else herea>li>
<li role="separator" class="divider">li>
<li><a href="#">Separated linka>li>
<li role="separator" class="divider">li>
<li><a href="#">One more separated linka>li>
ul>
li>
ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
div>
<button type="submit" class="btn btn-default">Submitbutton>
form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Linka>li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">状态<span class="caret">span>a>
<ul class="dropdown-menu">
<li><a href="/logout/">退出登入a>li>
ul>
li>
ul>
div>
div>
nav>
div>
<div>
<div class="jumbotron">
<h1>Hello, world!h1>
<p>...p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn morea>p>
div>
div>
body>
html>
0. 浏览器中访问 127.0.0.1:8000/index1 (清除cookie)
1. 触发登入检验 转到 login 登入页面中
2. 提交form表单的数据 (post请求)
3. 账户密码正确 设置cookie的值
4. 获取get后面携带的路径
5. 访问该路径
6. 点击退出按键
7. 清除cookie 并返回到 login 页面
3. session操作
3.1 表
session数据是保存在服务端的,给用户返回的是一个sessionid(键的随机字符串).
在默认的情况下操作session的时候需要一张 django_session表.
数据库迁移命令的时候django会自动创建.
3.2 session操作方法
1. 设置值
request.session{'key'} = value
2. 获取值
request.session.get('key')
3. 设置过期时间 (先设值之后设置过期时间)
request.session.set_expiry() 设置过期时间
括号内可以放四种类型的参数:
1.xx秒 整数
2.日期对象 到指定日期
3.数字零 浏览器关闭立刻失效
4.不写 失效时间取决于django内部全局session默认的失效时间
django默认的session默认的过期时间是14天,但是也可以认为的修改它
4. 清除值
request.session.delete() 只删服务端的 客户端不删
request.session.flush() 浏览器和服务端都清空 一般使用这个
3.3 测试
1. 数据库迁移
需要一张默认的表,
直接执行数据库迁移命令.
python manage.py makemigrations
python manage.py migrate
I:\备份\test\day08>python manage.py makemigrations
No changes detected # 没事
I:\备份\test\day08>python manage.py migrate
···ok
session 放在服务端的,位置可以有多种选择
1.MySQL
2.文件
3.redis
···
下载驱动后点测试,测试成功之后点ok.
2.设置值操作
url(r'^set_session/', views.set_session),
def set_session(request):
request.session['username'] = 'kid666'
return HttpResponse('set_session执行了')
浏览器中访问 127.0.0.1:8000/set_session
session的值也是在CooKie中查看
名称为 session 值是随机字符串
request.session['username'] = 'kid666'
内部发生的事情:
1. django内部会自动生成一个随机字符串
2. django自动将随机字符串和对应的数据存储到django_session表中
2.1 先在内存中产生操作数据的缓存
2.2 经过django中间件的时候才真正的操作数据库
3.将产生的随机字符串返回给客户端保存(键)
3. 获取值操作
url(r'^get_session', views.get_session)
def get_session(request):
value = request.session.get('username')
print(value)
return HttpResponse('get_session执行了')
print(request.session.get('username'))
内部发生的事情
1.自动从浏览器请求中获取session对应的随机字符串
2.拿着该字符串去django__session表中查询对应的数据
3.如果比对上了,则将对应的数据取出并以字典的形式封装到request.session中
如果比对不上request.session.get('') 返回的是None
4. 设置多个值
def set_session(request):
request.session['studio1'] = 'Python'
request.session['studio2'] = 'MySQL'
request.session['studio3'] = 'Linux'
return HttpResponse('set_session执行了')
上面的值存放在表中的一条数据中.
django_session表中数据是取决于浏览器的
同一个计算机上,同一浏览器只有一条数据生效.为了节省服务端资源.
(当session过期的时候会出现多条数据对应一个浏览器, 短暂的存在,会自动清理,也可以手动清理)
def get_session(request):
print(request.session)
print(request.session.get('studio1'))
print(request.session.get('studio2'))
print(request.session.get('studio3'))
return HttpResponse('哈哈哈')
0. 浏览器中访问 127.0.0.1:8000/set_session
1. 浏览器中访问 127.0.0.1:8000/get_session
get_session的执行结果:
<django.contrib.sessions.backends.db.SessionStore object at 0x000001CBA27C05C0>
Python
MySQL
Linux
5. 设置过期时间
request.session.set_expiry(3) 过期清理的是浏览器的数据,数据库的数据还存在
def set_session(request):
request.session['studio1'] = 'Python'
request.session.set_expiry(3)
return HttpResponse('set_session执行了')
def get_session(request):
print(request.session.get('studio1'))
return HttpResponse('get_session执行了')
0. 浏览器中访问 127.0.0.1:8000/set_session
1. 浏览器中访问 127.0.0.1:8000/get_session
get_session的执行结果:
None
6.多次设置
一条session数据可以存多个数据, 后面设置不会覆盖之前的设置.
def set_session(request):
request.session['studio1'] = 'Python'
return HttpResponse('set_session执行了')
def get_session(request):
print(request.session.get('studio1'))
print(request.session.get('studio2'))
return HttpResponse('get_session执行了')
6. 清除session
request.session.flush() 清理的是浏览器与数据库的数据.
def del_session(request):
request.session.flush()
return HttpResponse('已经清除缓存')
生成一个缓存
执行清除
7.登入功能
url(r'^login2/', views.login2),
url(r'^home2/', views.home2)
def login2(request):
forms_obj = MyForms()
if request.POST == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
forms_obj = MyForms(request.POST)
if username == 'kid' and password == '123':
request.session['username'] = 'kid666'
return redirect('/home2/')
return render(request, 'login.html', locals())
def home2(request):
if request.session.get('username') == 'kid666':
return HttpResponse('home2主页')
else:
return HttpResponse('非法访问')
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登入功能title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<form action="" method="post" novalidate>
{% for form in forms_obj %}
<p>{{ form.label }}:{{ form }} <span style="color: red;">{{ form.errors.0 }}span>p>
{% endfor %}
<input type="submit">
form>
div>
body>
html>
0. 在浏览器中访问 http://127.0.0.1:8000/home2/
1. 在浏览器中访问 http://127.0.0.1:8000/login2/
之后只要session不过去都可以直接访问 home2 主页
登入装饰器
url(r'^index4/', views.index4)
def login2(request):
forms_obj = MyForms()
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
forms_obj = MyForms(request.POST)
if username == 'kid' and password == '123':
target_url = request.GET.get('next')
request.session['username'] = 'kid666'
if target_url:
return redirect(target_url)
else:
return redirect('/home2/')
return render(request, 'login.html', locals())
def home2(request):
if request.session.get('username') == 'kid666':
return HttpResponse('home2主页')
else:
return HttpResponse('非法访问')
def decorator(func):
def inner(request, *args, **kwargs):
if request.session.get('username') == 'kid666':
res = func(request, *args, **kwargs)
return res
else:
target_url = request.get_full_path()
if target_url:
print(3)
return redirect(f'/login2/?next={target_url}')
else:
return redirect('/login2/')
return inner
@decorator
def index4(request):
return HttpResponse('index4页面')
4.CBV添加装饰器
4.1方式1
url(r'^mylogin/', views.MyLogin.as_view()),
from django.views import View
from django.utils.decorators import method_decorator
"""
CBV中django不建议直接给类的方法夹装饰器
无论该装神器能否正常工作都不建议直接加
"""
class MyLogin(View):
@method_decorator(login_auth)
def get(self, request):
return HttpResponse('我是get')
def post(self, request):
return HttpResponse('我是post')
4.2方式2
方式二:可以添加多个针对不同的方式加不同的装饰器.
from django.views import View
from django.utils.decorators import method_decorator
# 给多个属性加装饰器
@method_decorator(login_auth, name='set')
@method_decorator(login_auth, name='post')
class MyLogin(View): # 放装饰器的名字
def get(self, request):
return HttpResponse('我是get')
def post(self, request):
return HttpResponse('我是post')
4.3方式3
from django.views import View
from django.utils.decorators import method_decorator
class MyLogin(View):
@method_decorator(login_auth)
def dispath(self, request, *args, **kwargs):
pass
def get(self, request):
return HttpResponse('我是get')
def post(self, request):
return HttpResponse('我是post')