一,前言
1.1,什么是会话跟踪技术
在JavaWeb中,客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。在一个会话的多个请求*享数据,这就是会话跟踪技术。
例如在一个会话中的请求如下(请求银行主页):
- 请求登录(请求参数是用户名和密码)
- 请求转账(请求参数与转账相关的数据)
- 请求信誉卡还款(请求参数与还款相关的数据)
在上面会话中,当前用户信息必须在这个会话*享的,因为登录的是张三,那么在转账和还款时一定是相对张三的转账和还款!这就说明我们必须在一个会话过程中有共享数据的能力。
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求都应该属于同一个会话,而另一个用户的所有请求则该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时候购买的,这都是属于同一个会话,不能放在用户B或者用户C的购物车内,这不属于同一个会话。
1.2,会话路径技术使用Cookie 或 session 完成
我们知道HTTP协议是无状态协议,也就是说每个请求都会独立的! 无法记录前一次请求的状态。但是HTTP协议中可以使用Cookie来完成会话跟踪技术!
在JavaWeb开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
- cookie就是一段字符串,保存于本机电脑上
- session保存于服务器,用来保存用户的会话信息,依赖于Cookie
cookie是通过在客户端记录信息确定用户身份,Session是通过在服务端记录信息确定用户身份。
1.3,cookie和session机制
cookie机制采用的时在客户端保存状态的方案。作用就是为了解决HTTP协议无状态的缺陷所作的努力。
session机制采用的是一种在客户端与服务端之间保持状态的解决方案。由于采用服务器端保持状态的方案在客户端也需要保持一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。session还提供了方便管理全局变量的方式。session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分哪个用户的session。
1.4,cookie与session的实现原理
上图很明显的展示了Django的session和cookie的实现原理。服务器会生成两份相同的cookie字符串,一份保存在本地,一份发向请求的浏览器。浏览器将会受到的cookie字符串保存下来,当下次再发请求时,会将信息与这段cookie一同发送到服务器,服务器得到这段cookie会与本地保存的那份判断是否相同,如果相同就表示用户已经登录成功,保存用户登录成功的状态。Django的session保存在数据库中的数据相当于一个大字典,key为cookie的字符串,value仍是一个字典,字典的key和value为用户设置的相关信息,这样就可以方便的存取session里面的信息。
1.5,cookie和session内容
cookie内容主要包括:key,value,过期时间,路径和域。路径和域一起构成了cookie的作用范围。
cookie的使用由浏览器按照一定的规则在后台自动发送给服务器的,浏览器检查所在存储的cookie,如果某个cookie的作用范围大于等于将要请求的资源所在位置,将cookie附在请求资源的HTTP请求头上发给服务器。
cookie的生命周期跟过期时间相关,如果不设置过期时间,成为会话cookie,保存在内存中,关闭浏览器窗口,cookie消失。如果设置过期时间,浏览器会把cookie写入硬盘,这些cookie仍然有效,知道超过设定的过期时间。
存在硬盘上的cookie可以在同一个浏览器不同进程间共享,比如两个Chrome窗口。存在内存的,不同浏览器不同的处理方式。
session内容,服务器使用类似Python字典的key value 的形式存储,存储session方式很多,数据库,缓存,文件,加密cookie,数据库加缓存。
1.6,HTTP无状态性
HTTP被设计为“无状态”,每次请求都处于相同的空间中。在一次请求和下一次情况之间没有任何状态保持,我们无法根据请求的任何方面(IP地址,用户代理等)来识别同一人的连续请求,所以HTTP协议不具备保存之前发送过的请求或响应的功能。
协议对于事务处理没有记忆能力,对同一个URL请求没有上下文关系,每次的请求都是独立的,它的执行情况和结果与之前的请求和之后的请求时无直接关系的。它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况。服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。
如果说每一一种机制用来处理无状态,那么就需要花费时间不停的进行身份验证,正是这样,HTTP引入了session和cookie,即保持了HTTP的无状态性,也使得HTTP的应用称为有状态的。
二,Cookie概述
2.1,什么叫 Cookie
Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是 key-value 结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。
注意:cookie不能跨浏览器的,cookie中不能存在中文
Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端!
查看cookie
我们使用Chrome浏览器,打开开发者工具。
我们举个简单的例子。
代码如下:
modes.py
from django.db import models # Create your models here. class UserInfo(models.Model): user = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
我们给UserInfo表中插入一条记录,比如用户名和密码都是123(方便起见)。
views.py
from django.shortcuts import render, HttpResponse,redirect # Create your views here.
from cookie_demo.models import UserInfo def index(request):
if request.method == 'POST':
user = request.POST.get('user')
pwd = request.POST.get('pwd') user = UserInfo.objects.filter(user=user, pwd=pwd).first()
if user:
# 登录成功
'''
:return HttpResponse()
:return render()
:return redirect() 这三个都返回的是HTTPResponse,只不过render 和redirect 将其封装了 '''
response = HttpResponse("OK")
response.set_cookie('is_login', True)
return response else:
pass return render(request, 'cookie/index.html')
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body> <form action="" method="post">
{% csrf_token %}
<p>用户名:<input type="text" name="user"></p>
<p>密码:<input type="password" name="pwd"></p>
<input type="submit" value="submit"> </form> </body>
</html>
我们输入一个正确的用户名和密码,我们在前端cookie可以看到其信息:
在响应头,我们也可以看到其信息:
2.2,HTTP的Cookie规范
- Cookie大小上限为4KB;
- 一个服务器最多在客户端浏览器上保存20个Cookie;
- 一个浏览器最多保存300个Cookie
上面的数据只是HTTP的Cookie规范,但是在浏览器大战的今天,一些浏览器为了打败对手,为了展示自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现将你的硬盘占满的可能!
注意:不同浏览器之间是不共享Cookie的,也就是说在你使用IE访问服务器的时候,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。
2.3,Cookie的用途
- 服务器使用Cookie来跟踪客户端状态
- 保存购物车
- 显示上次登录名
2.4,Cookie与HTTP头
Cookie是通过HTTP请求和响应头在客户端和服务器端传递的:
- Cookie:
请求头,客户端发送给服务器端;
格式:Cookie:a=A;b=B;c=C。即多个Cookie用分号离开;
- Set-Cookie:
响应头,服务器端发送给客户端。
一个Cookie对象一个Set-Cookie:
Set-Cookie:a=A
Set-Cookie:b = B
Set-Cookie:c=C
2.5,Cookie的覆盖
如果服务器端发送重复的Cookie,那么会覆盖原有的Cookie。
cookies是浏览器为Web服务器存储的一小段信息。每次浏览器从某个服务器请求页面时,它向服务器会送之前收到的cookies,它保存在浏览器下的某个文件夹下。
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie:a=A;第二请求服务器端发送的是:Set-Cookie:a=AA,那么客户端只留下一个Cookie,即:a=AA。
2.6,Cookie 的生命周期
cookie不只有name和value,Cookie还有生命,所谓生命就是Cookie在客户端的有效时间,可以通过setMaxAge(int) 来设置Cookie的有效时间。以秒为单位。不设置默认为关闭窗口,Cookie结束。
- cookie. setMaxAge(-1):cookie的maxAge属性的默认值就是-1,表示只在浏览器内存中存活。一旦关闭浏览器窗口,那么cookie就会消失。
- cookie.setMaxAge(60*60):表示cookie对象可存活1小时。当生命大于0时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活1小时;
- cookie.setMaxAge(0):cookie生命等于0是一个特殊的值,它表示cookie被作废!也就是说,如果原来浏览器已经保存了这个Cookie,那么可以通过Cookie的setMaxAge(0)来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个Cookie。
过期时间
cookie 可以有过期时间,这样浏览器就知道什么时候可以删除cookie了。如果cookie没有设置过期时间,当用户关闭浏览器的时候,cookie就自动过期了。我们可以改变SESSION_EXPIRE_AT_BROWSER 的设置来控制session框架的这一行为。缺省情况下,SESSION_EXPIRE_AT_BROWSER 设置为False,这样,会话cookie可以在用户浏览器中保持有效达SESSION_COOKIE_AGE 秒(缺省设置是两周,即1209600秒)。如果你不想用户每次打开浏览器都必须重新登录的话,用这个参数来帮助你。如果SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True ,当浏览器关闭时,Django会使cookie失效。
SESSION_COOKIE_AGE :设置cookie在浏览器中存活的时间
在settings.py中添加:
2.7,Django中的cookie语法
cookies是一种数据存储技术,是将一段文本保存在客户端(浏览器)的一种技术,并且可以长时间的保存。
1,设置cookie
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect()
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
普通设置:
obj.set_cookie('tile' , 'name' , expires = value , path = '/')
加盐设置(普通cookie是明文传输的,可以直接在客户端直接打开,所以需要加盐,解盐之后才能查看):
obj.set_signed_cookie('k' , 'v' , salt = 'name' )
我们可以查看源码,发现HttpResponse() , render(),redirect() 都返回的是HttpResponse。
2,cookie参数介绍:
obj.set_cookie('tile', 'james', expires=value ,path='/',
domain=None, secure=False, httponly=False)
参数:
- 1,max_age = 1 : cookie生效的时间,单位是秒
- 2,expires:具体过期日期
- 3,path = '/' : 指定哪个url 可以访问到cookie,‘/’ 表示所有,即 path = '/'
- 4,domain = None(None代表当前域名):指定哪个域名以及它下面的二级域名(子域名)可以访问这个cookie
- 5,secure = False :https安全相关
- 6,httponly = False:限制只能通过HTTP传输,JS无法在传输中获取和修改。
class HttpResponseBase:
def set_cookie(self, key, #键
value = '', #值
max_age = None, #超长时间cookie需要延续的时间(以秒为单位)如果参数
#是\ None,这个cookie会延续到浏览器关闭为止。
expires = None, #超长时间,expires默认None, cookie失效的实际日期 / 时间。
path = '/', #Cookie生效的路径,浏览器只会把cookie回传给带有该路径的页面,
# 这样可以避免将 cookie传给站点中的其他的应用。
# / 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
domain = None, # Cookie生效的域名你可用这个参数来构造一个跨站cookie。
# 如, domain = ".example.com"所构造的cookie对下面这些站点都是可读的:
# www.example.com 、 www2.example.com和an.other.sub.domain.example.com 。
#如果该参数设置为None ,cookie只能由设置它的站点读取。secure = False,
# 如果设置为True ,浏览器将通过HTTPS来 回传cookie。
httponly = False #只能http协议传输,无法被JavaScript获取
# (不是绝对,底层抓包可以获取到也可以被覆盖)
): pass
3,获取cookie
普通获取:
request.COOKIES.get('k')
加盐获取:
cookies = request.get_signed_cookie('k' , salt = 'name' )
4,删除cookie
response.delete_cookie("cookie_key",path="/",domain=name)
代码:
def logout(request):
rep = redirect("/login/")
# 删除用户浏览器上之前设置的user cookie的值
rep.delete_cookie("user")
return rep
5,尝试给每个视图函数装饰cookie认证功能
from django.shortcuts import render,redirect,HttpResponse
import datetime
from until import mysqlhelper # Create your views here. def cookie_auth(func):
def weaper(request, *args, **kwargs):
cookies = request.get_signed_cookie('k', salt='james')
if cookies == 'v':
return func(request)
else:
return HttpResponse("NG")
return weaper now = datetime.datetime.utcnow()
delta = datetime.timedelta(seconds=10) def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
username = request.POST.get('N')
password = request.POST.get('P')
if username == 'james' and password == '123':
obj = redirect('/modal')
# obj.set_cookie("tile", "james", max_age = 1,, )
value = now + delta
obj.set_cookie('tile', 'james', expires=value ,path='/',
domain=None, secure=False, httponly=False)
obj.set_signed_cookie('k', 'v', salt='james', )
return obj
else:
return render(request, 'login.html')
def test(request):
return render(request, 'layout.html') @cookie_auth
def modal(request):
sql = '''
SELECT teacher.id as tid,teacher.`name`as tname,class.title FROM day64.teacher
LEFT JOIN teacher_class ON day64.teacher.id=day64.teacher_class.tid
LEFT JOIN day64.class ON day64.teacher_class.cid=day64.class.id;
'''
teacher_list = mysqlhelper.get_list(sql, [])
res = {}
for row in teacher_list:
tid = row['tid']
if tid in res:
res[tid]['titles'].append(row['title'])
else:
res[tid] = {'tid':row["tid"],'tname':row["tname"],'titles':[row["title"],]} class_list=mysqlhelper.get_list("SELECT id ,title FROM day64.class" ,[])
return render(request,'modal.html',{"list":res.values(),"class_list":class_list} )
2.8,实例:保存上次访问时间
我们要做的就是显示上次访问时间。
views.py
from django.shortcuts import render, HttpResponse,redirect # Create your views here.
from cookie_demo.models import UserInfo def login(request):
if request.method == 'POST':
user = request.POST.get('user')
pwd = request.POST.get('pwd') user = UserInfo.objects.filter(user=user, pwd=pwd).first()
if user:
# 登录成功
response = HttpResponse("OK")
response.set_cookie('is_login', True) import datetime
date = datetime.datetime(year=2019, month=5, day=28, hour=9, minute=17)
response.set_cookie('username', user.user,expires=date)
return response else:
pass return render(request, 'cookie/login.html') def index(request):
print('index: ', request.COOKIES)
is_login = request.COOKIES.get('is_login') if is_login:
username = request.COOKIES.get('username')
import datetime
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') last_time = request.COOKIES.get('last_visit_time', "")
response = render(request, 'cookie/index.html', {'username': username, 'last_time': last_time})
response.set_cookie('last_visit_time', now)
return response
else:
return redirect('/cookie/login/')
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <h3>Hi, {{ username }}</h3>
<p>上次登录时间: {{ last_time }}</p> </body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body> <form action="" method="post">
{% csrf_token %}
<p>用户名:<input type="text" name="user"></p>
<p>密码:<input type="password" name="pwd"></p>
<input type="submit" value="submit"> </form> </body>
</html>
效果:
2.9,实例:Cookie版登陆校验
views视图代码:
from django.shortcuts import render, HttpResponse, redirect
import datetime
# Create your views here. # 登录验证装饰器
def login_auth(func):
def inner(request, *args, **kwargs):
# 获取请求的全路径
url = request.get_full_path()
# /shopping/?nana=ppp
# 服务器先拿到cookie,如果拿到则直接跳转到shopping或者order视图页面
is_login = request.COOKIES.get('is_login')
# xxx = request.get_signed_cookie('xxx' ,salt='123')
# 拿到cookie 判断是不是登录状态
if is_login:
# 是登录状态直接调用被装饰的函数,返回我们想要的页面
ret = func(request, *args, **kwargs)
else:
# 服务端如果拿不到正确的cookie就会重定向了login这个页面,但是带了一些参数
return redirect('/login/?next=%s'%url)
# 当清楚浏览器缓存是直接登录http://127.0.0.1:8000/shopping/,会跳转到登录页面
# 并且在url后面拼上http://127.0.0.1:8000/login/?next=/shopping/
# 127.0.0.1:8000/login/?next=/shopping/?nana=ppp
return ret
return inner def login(request):
if request.method == 'POST':
# 装饰器中redirect('/login/?next=%s'%url)通过get就可以拿到next的值
# url = request.GET.get('name')
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'james' and password == '123':
# 登录成功后跳转到该页面
# obj = redirect(url)
obj = HttpResponse("登录成功")
# 当我们不指定path时,被装饰的视图函数页面只登录后都可以直接被访问,而不需要重新登录
obj.set_cookie('is_login', True)
# 浏览器只会把cookie会传给带有该路径的页面
# 也就说当我们登录成功后,访问shopping页面时可以直接登录
# obj.set_cookie('is_login', True, path='/shopping/')
# 但是我们访问order页面时,仍然会跳到登录页面让我们重新登录
now = datetime.datetime.now().strftime('%Y-%m-%d %X')
# 设置cookie,在浏览器的请求头中的cookie中我们可以看到我们设置的额cookie
# 并将登录时间也写到cookie中
obj.set_cookie('last_time', now)
# 在浏览器中打开可以看到一个key
obj.set_cookie('username','james')
obj.set_cookie('password','123')
# 获取cookie,并对cookie进行加盐,加盐后的123 是进行加密的遗传字符吗
obj.set_signed_cookie('james','durant',salt='123')
# 登录成功后对其进行重定向
return obj
else:
obj = HttpResponse("登录失败")
return obj
return render(request, 'login.html') # 退出登录
def logout(request):
obj = HttpResponse("注销成功")
# 登录成功获得服务端发送来的cookie,删除cookie,
# 这样我么在浏览器中在输入 http://127.0.0.1:8000/shopping/
# J就会在此跳到登录页面让我门重新登录
return obj # 购物页面
def shopping(request):
return render(request, 'shopping.html', locals())
# locals()用法:locals()可以直接将函数中所有变量全部传给模板
# 当然这可能会传递一些多余的参数,有点浪费内存的嫌疑 # 登录成功后再请求shopping页面,在请求头就可以看到cookie中带有键值对的形式:
# Cookie: csrftoken = ybQexReWXIWaO0If0p0I7NubfBSvjUlSfR2EWWX8eKCwXbL8lDo4Kk3ar6Nbr5ZR;
# last_time = "2018-09-13 18:13:17";
# name = james;
# pwd = 123;
# xxx = xxxxxx:1g0Ocf: 1kRB9Q0FVXlVzstrRbm4fJ61eF0 # 订单页面
@login_auth
def order(request):
return render(request, 'order.html', locals())
url路由配置
from django.contrib import admin
from django.conf.urls import url
# 需要先导入对应的app的views文件
from cookieapp import views urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.login),
url(r'^order/', views.order),
url(r'^shopping/',views.shopping),
url(r'^logout/', views.logout),
]
template模板代码
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <form action="" method="POST">
<p>用户名:<input type="text" name="username"></p>
<p>密码: <input type="password" name="password"></p>
<input type="submit">
</form> </body>
</html>
shopping.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <h1>购物车页面</h1> </body>
</html>
order.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <H1>订单页面</H1> </body>
</html>
结果展示:(登陆页面:127.0.0.1:8000/login.login)
登陆成功:
cookie展示
三,session概述
session就是在服务器端的“Cookie”,将用户数据保存在服务器端,远比保存在用户端要安全,方便和快捷的多。Session依赖于Cookie,但是与Cookie不同的地方就是在于session将所有的数据都放在服务器端,用户浏览器的cookie中只会保存一个非明文的识别信息,比如哈希值。
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问该服务器中的其他web资源时,其他web资源再从用户各自的session中取出数据为用户服务。
Django的Session机器会向请求的浏览器发送cookie字符串。同时也会保存在本地一份,用来验证浏览器登录是否为同一用户。他存在于服务器,Django默认会把session存入数据库中。
Session依赖于Cookie,如果浏览器不能保存cookie,那么session就失效了。因为他需要浏览器的cookie和session做对比。session就是用来在服务器端保存用户的会话状态。
3.1,Django中session语法
class backends.base.SessionBase
# 这是所有会话对象的基类,包含标准的字典方法:
__getitem__(key)
Example: fav_color = request.session['fav_color']
__setitem__(key, value)
Example: request.session['fav_color'] = 'blue'
__delitem__(key)
Example: del request.session['fav_color'] # 如果不存在会抛出异常
__contains__(key)
Example: 'fav_color' in request.session
get(key, default=None)
Example: fav_color = request.session.get('fav_color', 'red')
pop(key, default=__not_given)
Example: fav_color = request.session.pop('fav_color', 'blue')
类似于字典数据类型的内置方法
keys()
items()
setdefault()
clear() # 它还有下面的方法:
flush()
# 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。 set_test_cookie()
# 设置一个测试cookie,用于探测用户浏览器是否支持cookies。由于cookie的
工作机制,你只有在下次用户请求的时候才可以测试。 test_cookie_worked()
# 返回True或者False,取决于用户的浏览器是否接受测试cookie。你必须在之
前先调用set_test_cookie()方法。 delete_test_cookie()
# 删除测试cookie。 set_expiry(value)
# 设置cookie的有效期。可以传递不同类型的参数值:
• 如果值是一个整数,session将在对应的秒数后失效。例如request.session.
set_expiry(300) 将在300秒后失效.
• 如果值是一个datetime或者timedelta对象, 会话将在指定的日期失效
• 如果为0,在用户关闭浏览器后失效
• 如果为None,则将使用全局会话失效策略
失效时间从上一次会话被修改的时刻开始计时。 get_expiry_age()
# 返回多少秒后失效的秒数。对于没有自定义失效时间的会话,这等同于SESSION_COOKIE_AGE.
# 这个方法接受2个可选的关键字参数
• modification:会话的最后修改时间(datetime对象)。默认是当前时间。
•expiry: 会话失效信息,可以是datetime对象,也可以是int或None get_expiry_date()
# 和上面的方法类似,只是返回的是日期 get_expire_at_browser_close()
# 返回True或False,根据用户会话是否是浏览器关闭后就结束。 clear_expired()
# 删除已经失效的会话数据。
cycle_key()
# 创建一个新的会话秘钥用于保持当前的会话数据。django.contrib.auth.login()
会调用这个方法
- 获取session:request.session[key]
- 设置session:request.session[key] = value
- 删除session:del request[key]
1、设置Sessions值
request.session['session_name'] ="admin" 2、获取Sessions值
session_name = request.session["session_name"] 3、删除Sessions值
del request.session["session_name"] 4、flush()
删除当前的会话数据并删除会话的Cookie。
这用于确保前面的会话数据不可以再次被用户的浏览器访问 5、get(key, default=None)
fav_color = request.session.get('fav_color', 'red') 6、pop(key)
fav_color = request.session.pop('fav_color') 7、keys() 8、items() 9、setdefault() 10 用户session的随机字符串
用户session的随机字符串举例:
request.session.session_key # 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否
request.session.exists("session_key") # 删除当前用户的所有Session数据
request.session.delete("session_key") request.session.set_expiry(value)
*如果value是个整数,session会在些秒数后失效。
*如果value是个datatime或timedelta,session就会在这个时间后失效。
*如果value是0, 用户关闭浏览器session就会失效。
*如果value是None, session会依赖全局session失效策略。
3.2,session配置
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid"
# Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/"
# Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None
# Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False
# 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True
# 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600
# Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False
# 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False
# 是否每次请求都保存Session,默认修改之后才保存(默认)
3.3,session的作用
session下次通过cookie中的sessionID(键)获取用户信息值(值)
1,会话保持,记住用户的登录状态(WEB网站,分布式架构)
2,避免了敏感信息保存在客户端,防止客户端修改cookie信息(和cookie的区别)
session的过期时间
session的过期时间:django默认设置是2周 ,如果session过期,浏览器再携带之前的cookie就不能免登陆了。因为cookie已经失效了。
前端:如果超出了session的过期时间,那么浏览器会自动将对应的cookie删除掉
后端:django没有对过期的session做任何处理
如何删除后台保留的一些过期的session信息
python manage.py clearsessions
当然,如果用户在过期时间内主动退出登录,那么django会将该用户对应的session数据给删除掉 (request.session.flush())
但是如果用户在登录完以后没有主动退出,并且超出了过期时间,用户需要重新登录,但django中的过期session是不清除的,需要定期清理过期的session数据。
3.4,session ID的作用
当客户端第一次请求session时,服务器端会为客户端创造一个session对象,并且生成一个session ID (通过加密算法)。然后保存在cookie中,当用户再次登录时,客户端通过cookie,将session ID传到服务器,去和服务器中的session ID进行比对,寻找这个session,然后根据查找结果执行对应的操作。
因为session ID 是保存在cookie中,而cookie是存在于客户端,所以session ID 并不安全。
为了避免session ID 被盗,我们可以这样做:
- 1,敏感操作需要用户输入密码来进行二次认证
- 2,网站HTTPS化,提高消息传递过程中的安全系数
- 3,用户使用一个密钥对参数进行hash,这样即使cookie被盗取,也会因为没有密钥而无法获取sessionID。
3.5,session的保存方式
Session是大多数网站都需要具备的功能,Django为我们提供了一个通用的session框架,并且可以使用多种session数据的保存方式:
- 保存在数据库内
- 保存到缓存
- 保存到文件内
- 保存到cookie内
通常情况下,没有特别需求的话,请使用保存在数据库内的方式,尽量不要保存在cookie内。
django的session框架支持匿名会话,封装了cookies的发送和接收过程。cookie包含一个会话ID而不是数据本身(除非你使用的是基于后端的cookie)。
django的会话框架完全的,唯一的基于cookie。它不像PHP一样,把会话的ID放在URL中,那样不仅使得URL变得丑陋,而且使得你的网站易于受到通过“Referer”头部进行窃取会话ID的攻击。
3.6,session流程解析
session的应用要依赖于cookie(session就是cookie的变种)
1,每次用户第一次访问服务端,把用户的唯一字符串session_id加到cookie里面发送给客户端;
当用户登录之后,生成一个字典{key : value},将字典存入session,key是自动生成的一段字符串表示,返回cookie,value是一个自定义格式的字典。
2,服务器端保存随机字符串(sessionID :{用户信息}) 服务端
当我们在django中用到session时,cookie由服务端随机生成,写到浏览器的cookie中。每个浏览器都有自己的cookie值,是session寻找用户信息的唯一标识。每个浏览器请求到后台接受的request.session 等价于在1中的session字典key (cookie)对应的value。
3,session的好处就是客户端只有cookie的值,但是始终没有用户信息。
用法:request.session.get('k1') request.session['k1'] = 'v1'
session依赖于cookie,cookie保存在浏览器。session保存在服务器 端。
3.7,示例
注意:这里需要注意在session中,我们可以设置多个key:value的值,方便我们做很多事情,比如判断哪个用户:
如果用户登录后,那么他肯定有一个cookie,而当他访问购物车的时候,怎么判断是哪个用户呢?我们可以在session设置,当用户登录的时候,我们把用户名增加到session中,那么用户携带cookie访问的时候,我们就能判断是哪个用户来访问的。
比如下面的对应关系:
user1
cookie : 001
server session(举例格式)
{session:001 {'IS_LOGIN':'True',username:'james'}}
3.7.1,一段简单的Django中实现session的代码,判断用户是否已经成功登陆
from django.shortcuts import render,redirect,HttpResponse # Create your views here. def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'james' and password == '123':
# 设置session
request.session['IS_Login'] = True
return redirect('/app01/home') return render(request, 'login.html') def home(request):
# 获取session
is_login = request.session.get("IS_LOGIN", False)
if is_login:
return HttpResponse('order')
else:
return redirect('/app01/login/')
3.7.2 在视图中使用session
def session_test(request):
# 设置session值,会将其存到db.sqlite数据库中的django_session表中
# 每发送一次请求就会在django_session表中添加一条记录
request.session['username'] = 'james'
request.session['is_login'] = True
'''
1,生成一个随机字符串
2,把随机字符串以cookie的形式写回给浏览器
3,会在数据库中存{'随机字符串': {'username' : 'james' , 'is_login': True}
'''
return HttpResponse("ok") def get_session(request):
# 在浏览器中输入路由地址就会访问访问该视图函数
# 获取session值,但是一定要先获取session值,否则获取session时会报错
# name=request.session['username']
# is_login=request.session['is_login']
# print(name)
# print(is_login)
# session取值的两种方式,字典key或者get进行取值
# request.session['k1']
# request.session.get('k1', None)
# 设置session的值
# request.session['k1'] = 123 # name=request.session.setdefault('username', 123)
# 如果session中的username有值就不会设置,无值则进行设置
# print(name) #-----123
# del request.session['k1']
# 通过key将session的值进行删除
# print(request.session.session_key)
# 取出表django_session表中session_key字段对应的值
# 将所有Session失效日期小于当前日期的数据删除
# request.session.clear_expired()
# 判断session表中session_key字段的值是否存在,返回值为布尔值
# print(request.session.exists("b16mh23xajc2u69vazvivf8ruo4ilumi"))
# 会删除数据库的session表中的session的记录,但是浏览器中的session id还在
# request.session.delete()
# 删除当前的会话数据并删除会话的Cookie。
# 通常用它,他会把数据库以及浏览器会话中的session id都删除
# (调用视图函数test可以看到效果的确是已经被删除) request.session.flush()
return HttpResponse("OK")
flush()方法是比较安全的一种做法,而且一次性将session中的所有内容全部清空,确保不留后患,但是也有不好的地方,那就是如果你在session中夹带了一点“私货”,会被一并删除,这一点一定要注意。
比如登出视图函数:
def logout(request):
if not request.session.get('is_login', None):
# 如果本来就未登录,也就没有登出一说
return redirect("/index/")
request.session.flush()
# 或者使用下面的方法
# del request.session['is_login']
# del request.session['user_id']
# del request.session['user_name']
return redirect("/index/")
3.7.3 在视图函数中使用装饰器装饰CBV
from django.utils.decorators import method_decorator
# 在视图函数中装饰类 需要先导入method_decorator模块
from django.views import View # 在类上加装饰器需要指定name的值,也就是要指定装饰类内的那个函数
# 当我们登录成功后就会直接跳转到我们要登录的页面
# @method_decorator(login_auth, name='post')
# @method_decorator(login_auth, name='get') class MyOrder(View):
# 将装饰器传入进去
# @method_decorator(login_auth)
# 我们可以重写dispatch函数来实现
def dispatch(self, request, *args, **kwargs):
# 我们可以重写dispatch函数来实现类似于装饰器的效果
# dispatch 内部根据反射来实现函数执行
# 集成父类view的属性
ret = super().dispatch(request, *args, **kwargs)
return ret
# 在cbv上加装饰器,需要用method_decorator修饰一下
@method_decorator(login_auth)
def get(self, request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post') '''
1 导入from django.utils.decorators import method_decorator
2 加载get,post,dispatch方法上:@method_decorator(login_auth)
3 加在类上:@method_decorator(login_auth,name='get')
'''
四,cookie + session
1,cookie引入session
cookie看似解决了HTTP(短连接,无状态)的会话保持问题,但把全部用户数据保存在客户端,存在安全隐患。
于是cookie+session出现了,我们,我们可以把关于用户的数据保存在服务端,在客户端cookie里加一个sessionID(随机字符串)。
基于以上原因:cookie+session组成就成立了,并且结束了单单使用cookie做会话保持的方式。
2,Session和Cookie的好处
使用Session和Cookie的好处:Cookie可以理解为一个身份证ID,你只要拿着他去和Server端进行通信,如果你没有这个ID,那么server端也不知道你是谁。
举个例子:我在写博客的时候,在做Cookie和Session的实验,把Cookie删掉了,当我保存的时候直接给我提出来了,为什么呢?就是因为server端不知道我是谁了,因为我已经没有秘钥了。
所以,只要Session和Cookie任意一方失效,就可以理解为:Cookie失效相当于身份证ID过期,需要重新认证才可以继续使用。Session失效就相当于银行里的数据标识此ID无效,也需要重新申请。
3,cookie+session 的工作流程
1,当用户来访问服务端的时候,服务端生成一个随机字符串;
2,当用户登录成功后,把{sessionID : 随机字符串} 组织成键值对 加到cookie里发送给用户;
3,服务器以发送给客户端cookie中的随机字符串做键,用户信息做值,保存用户信息;
代码梳理
用户登录的两种方式:
1,使用login() 和logout()这两个内置函数实现登录和退出;缺点就是用户的登录时在操作同一个request.user。导致同一台电脑上不能同时登录两个用户;
2,如果同一台电脑的同一个网站,登录多个账号,为了防止串号,不能再使用login() 和logout()函数了,可以通过session和cookie来实现这个需求;
1,login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <form action="login.html" method="post">
<input type="text" name="username">
<input type="password" name="pwd">
<input type="submit" value="submit">
</form> </body>
</html>
2,home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title> <style>
.header{
height: 48px;
}
</style> </head>
<body> <div class="header">
<div style="float: right;">{{ username }}</div>
<div style="float:right;"><a href="logout.html">注销</a></div>
</div> <div style="height: 500px; background-color: #ddd"></div> </body>
</html>
3,view.py
from django.shortcuts import render,redirect,HttpResponse # Create your views here. def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'james' and password == '123':
# 设置session
request.session['IS_Login'] = True
request.session['USERNAME'] = 'james'
return redirect('/Manytable/home/')
elif username == 'durant' and password == '123':
request.session['IS_LOGIN'] = True
request.session['USERNAME'] = 'durant'
return redirect('/Manytable/home/') return render(request, 'Manytable/login.html') def home(request):
# 获取session
is_login = request.session.get("IS_LOGIN", False)
if is_login:
username = request.session.get("USERNAME", False)
return render(request, 'Manytable/home.html', {'username':username})
# return HttpResponse('order')
else:
return redirect('/Manytable/login/')
4,mysite/urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url
# 需要先导入对应的app的views文件 from Manytable import views # admin 后台的路由,先注释掉
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 你的路由,重点是引号中的正则表达式和后面的业务逻辑函数
url(r'^login/',views.login),
url(r'^index/',views.home),
]
5,格式如下:
作业练习
1,登录案例
需要的页面:
#login.html:登录页面,提供登录表单; #index1.html:主页,显示当前用户名称,如果没有登录,显示您还没登录; #index2.html:主页,显示当前用户名称,如果没有登录,显示您还没登录;
思考:如果第二个人在同一个浏览器上登录,Django-session表会怎么样呢?
2,验证码案例
验证码可以去识别发出请求的是人还是程序!当然,如果聪明的程序可以去分析验证码图片!但是分析图片也不是一件容易的事情,因为一般验证码图片都会带有干扰线,人都看不清,那么程序一定分析不出来。
参考文献:https://www.cnblogs.com/chenchao1990/p/5283725.html
https://blog.csdn.net/qq_32446743/article/details/79482536
https://www.jianshu.com/p/59cb3ecd81a4
https://www.cnblogs.com/sui776265233/p/9643055.html