「Django框架」-会话技术之cookie与session
lipiwang 2024-11-05 11:54 23 浏览 0 评论
文章来源于公众号【Python野路子】
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
会话跟踪技术
什么是会话跟踪技术
我们可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应。
例如,我们给10086打电话,我就是客户端,而10086服务人员就是服务端。从双方接通电话那一刻起,会话就开始了,到某一方挂断电话标识会话结束。在通话过程中,我们会向10086发出多个请求,那么多个请求都在一个会话中。
在Web中,客户使用浏览器向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。
在一个会话的多个请求中共享数据,这就是会话跟踪技术。
例如,在一个会话中的请求如下:请求银行主页。
1)请求登录(请求参数是用户名和密码);
2)请求转账(请求参数与转账相关的数据);
3)请求信用卡还款(请求参数与还款相关的数据)。
在上面会话中,当前用户信息必须在这个会话中共享的,因为登录的是张三,那么在转账和还款时一定是张三的转账和还款,这就说明我们必须在一个会话过程中共享数据的能力。
会话路径技术使用Cookie或session完成
在Web应用中,HTTP协议是无状态的,即每次请求都是独立的,无法记录前一次请求的状态,每次请求都是一次新的请求。
无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。
但是,有时候我们需要知道上次请求状态,比如用户是否登录过,浏览过哪些商品等。可以使用会话跟踪,可以使用Cookie、Session、token(自定义的session)。
Cookie
什么叫做Cookie
Cookie,翻译成中文小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。
Cookie是客户端会话技术,数据都存储在客户端,以key-value进行存储。支持过期时间max_age,默认请求会携带本网站的所有cookie,cookie不能跨域名,不能跨浏览器,cookie默认不支持中文,base64。
Cookie是由服务器创建,通过服务端的响应发送给客户端浏览器的一个键值对,然后浏览器会把Cookie保存起来,并标注出Cookie的来源(哪个服务器的Cookie),当客户端下一次再访问服务器时,默认请求会携带本这个服务器的所有Cookie,这样服务器就可以识别客户端了。
设置cookie应该是服务器response,获取cookie应该在浏览器request,删除cookie应该在服务器response。
Cookie规范
- Cookie大小上限为4KB;
- 一个服务器最多在客户端浏览器上保存20个Cookie;
- 一个浏览器最多保存300个Cookie;
上面的数据只是HTTP的Cookie规范,但是,在浏览器百家争鸣的当下,一些浏览器为了争夺一份份额,展现自己的优势,可能对Cookie的规范扩展,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能!
注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器
Cookie与HTTP头
Cookie是通过HTTP请求头和响应头在客户端和服务器端传递的:
- Set-Cookie:响应头,服务器端发送给客户端;
- Cookie:请求头,客户端发送给服务器端;格式:Cookie: a=A; b=B; c=C。即多个Cookie用分号隔开;
- 一个Cookie对象一个Set-Cookie:Set-Cookie: a=A Set-Cookie: b=B Set-Cookie: c=C;
Cookie的覆盖
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。
请求流程
第一次请求:
① 浏览器第一次请求服务器的时候,不会携带任何cookie信息,,请求头中没有任何cookie信息;
② 当服务器接受到这个请求之后,会做一些验证,例如进行用户名和密码的验证,验证没有问题则可以设置cookie信息。
③ 服务器会为响应设置cookie信息,响应头中有set_cookie信息。
③ 我们的浏览器接收到这个响应之后,发现响应中有cookie信息,浏览器会将cookie信息保存起来。
后续请求:① 第二次或之后的请求都会携带cookie信息,请求头中有cookie信息;
② 服务器接受到请求之后,会发现请求中携带cookie信息,这样就认识是谁的请求了。
代码实现
login.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录界面</title>
<style>
#loginbox{
width: 350px;
height: 200px;
border: 1px solid red;
margin: 90px auto;
text-align: center;
}
</style>
</head>
<body>
<div id="loginbox">
<h2>登录界面</h2>
<form action="" method="post">
{% csrf_token %}
帐 号:<input type="text" name="username">
<br/>
密 码:<input type="password" name="password">
<br/>
<br/>
<input type="submit" value="登录">
<input type="button" value="注册">
</form>
</div>
</body>
home.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
欢迎
{% if username %}
{{ username }}
{% else %}
游客
{% endif %}
访问~~
<br>
<a href="{% url 'user:logout' %}">退出</a>
</body>
</html>
views.py:
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username and password: # 此处为了方便测试,没有去数据库进行校验,实际的需要去数据库校验用户名和密码之类的
'''
响应体:
return HttpResponse()
return render()
return redirect()
都可以
'''
response = redirect(reverse('user:home'))
# 设置cookie信息
response.set_cookie('username', username)
is_login = True
response.set_cookie('is_login', is_login)
return response
return render(request, 'login.html')
def home(request):
# 获取cookie信息
is_login = request.COOKIES.get('is_login')
username = request.COOKIES.get('username')
if is_login:
return render(request, 'home.html', {'username': username})
return redirect(reverse('user:login'))
def logout(request):
response = redirect(reverse('user:login'))
# 删除cookie信息
response.delete_cookie('is_login')
response.delete_cookie('username') # 注销,最好都删除,避免注册之后下次还会携带未删除的信息
return response
我们可以看到第一次打开登录页面的时候,不携带Cookie信息(csrf暂忽略):
我们再登录,即提交表单之后,服务器设置了Cookie(可参考上面设置Cookie信息代码):
退出,即删除Cookie信息,这里只删除is_login的信息
上面是以明文的方式显示,我们可以进行加盐设置,以加密形式显示:
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
username = request.POST.get('username')
response = redirect(reverse('user:home'))
#response.set_cookie('username', username)
#set_signed_cookie(key,value,salt='加密盐',...)
response.set_signed_cookie('username', username, 'qmpython')
return response
def home(request):
username = request.COOKIES.get('username')
# 解密
#username = request.get_signed_cookie('username', 'qmpython')
response = HttpResponse(f'{username}登录成功')
return response
Cookie:具体一个浏览器针对一个服务器存储key-value。
注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用Chrome访问服务器时,不可能把IE保存的Cookie发送给服务器。
同一个浏览器访问A网站,又去访问B网站,不可能将A的cookie信息携带发送给B服务器。
默认情况下,Cookie只在浏览器的内存中存活,也就是说,当你关闭浏览器后,Cookie就会消失!
问题一:如果只是关闭网站(关闭标签页),没有退出浏览器呢?
A:并不会,如果在不关闭浏览器,只关闭页面时,清除cookie。可以使用js事件处理:
<script>
window.onunload = function(){
//窗口关闭
//在这发一个ajax请求,后台清除cookie
}
</script>
set_cookie参数:
class HttpResponseBase:
def set_cookie(self,
key, # 键
value='', # 值
max_age=None, # cookie有效时长,单位为秒,默认为None表示关闭浏览器失效,指定 为有效数值100表示100秒后自动失效
expires=None, # 支持一个datetime或timedelta,可以指定一个具体的日期, expires=timedelta(days=10)表示十天后过期。
# max-age和exepires两个指定一个。
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协议传输,无法被js获取(不是绝对的,底层抓包可以获取到也可以被覆盖)
samesite=None
):pass
练习:使用cookie实现上次登录时间
def home(request):
# 获取cookie信息
username = request.COOKIES.get('username')
last_login_time = request.COOKIES.get('last_login_time', '')
response = render(request, 'home.html', {'username': username, 'last_login_time': last_login_time})
from datetime import datetime
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
response.set_cookie('last_login_time', now)
return response
Session
前面介绍了Cookie,为什么还需要Session呢?其实很多情况下,只使用Cookie便能完成大部分需求。但是,只使用Cookie往往是不够的,考虑用户登录信息或一些重要的敏感信息,用Cookie存储的话会带来一些问题,最明显的是由于Cookie会把信息保存到本地,因此信息的安全性可能受到威胁。Session的出现很好地解决的这个问题,Session与Cookie类似。
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问该服务器中的其他web资源时,其他WEB资源再从用户各自session中取出数据为用户服务。
Session是服务端会话技术,依赖于Cookie,如果在浏览器中**禁用cookie**的话,那么session就失效了,因为它需要浏览器的cookie值去session里做对比。
1. 启用Session
Django默认启用Session,可以在设置文件settings.py:
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
]
如果禁用session,则注释即可。
2. 存储方式
在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。
2.1 数据库
默认是存储在数据库中的。
SESSION_ENGINE='django.contrib.sessions.backends.db'
如果存储在数据库中,则需要安装Session应用,默认已设置:
INSTALLED_APPS = [
'django.contrib.sessions',
]
在进行数据库迁移的时候,Django自动帮我们生成了django_session表,有3个字段,分别为session_key,session_data,expris_date,Django中session的默认过期时间是14天。
迁移数据库后,就会生成django_session表:
3. 请求流程
第一次请求:
① 我们第一次请求的时候可能会携带一些信息(例如用户名/密码),cookie中没有任何信息。
② 当服务器接受到这个请求之后,会做一些验证,例如进行用户名和密码的验证,验证没有问题则可以设置session信息。
③ 在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个sessionid的cookie信息。
④ 客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存sessionid的信息)。
后续请求:
① 第二次或之后的请求都会携带sessionid的cookie信息
② 当服务器接收到这个请求之后,获取到sessionid信息,然后进行验证,验证成功,则可以获取session信息(session信息保存在服务器端)
4. 代码实现
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
'''
响应体:
return HttpResponse()
return render()
return redirect()
都可以
'''
username = request.POST.get('username')
password = request.POST.get('password')
if username and password: # 此处为了方便测试,没有去数据库进行校验,实际的需要去数据库校验用户名和密码之类的
# 设置session信息
request.session['is_login'] = True
request.session['username'] = username
'''
以上设置,实际执行以下操作:
1、生成随机字符串,例如:123abc!@#
2、response.set_cookie("sessionid", 123abc!@#)
3、在django_session表中创建一条记录:
session_key session_data
123abc!@# {"is_login":True, "username": "admin"}
'''
return redirect(reverse('user:home'))
def home(request):
is_login = request.session['is_login']
username = request.session['username']
'''
以上执行以下操作:
1) request.COOKIES.get('sessionid'),获得session_key
2) 通过上面得到的session_key,在django_session表中获取对应的session_data
3) 从session_data获取is_login值。
'''
if is_login:
return render(request, 'home.html', {'username': username})
return redirect(reverse('user:login'))
def logout(request):
response = redirect(reverse('user:login'))
# 删除session数据,即删除了session_data字段值中对应键和值,但是cookie信息还存在,请求还会携带cookie信息,也就成了脏数据,通过cookie再找session,已经找不到了。
#del request.session['is_login']
# 清除所有session,禁用!!!
request.session.clear()
# 清除当前的会话数据并删除会话的cookie,session,cookie一起干掉,删除session表中session的整条数据。
request.session.flush()
'''
执行以下操作:
1) request.COOKIE.get('sessionid'),获得session_key
2) django_session表中删除session_key对应的记录
3) response.delete_cookie('sessionid')
'''
return response
打开登录页面,没有携带任何cookie信息(csrf那个先不管)
发起登录请求,设置session信息,其中会生成随机字符串比如mjx9gsq47ofwmcg2ubxtg28uk6idbq2a,然后以这个sessionid为key,这个字符串为value,设置cookie信息。
并且在django_session插入记录,其中session_key字段的值就是sessionid的值,session_data字段的值就是上面设置的session键值对。
然后跳转到home页,就会携带cookie信息,获取sessionid的值,在数据库django_session表中查找是否有相关记录。
如果退出,则删除session信息
从DJANGO角度来看:
第一次请求:
① 第一次请求,在请求头中没有携带任何cookie信息。
② 我们在设置session的时候,session会做2件事。
第一件:将数据保存在数据库中。
第二件:设置一个cookie信息,这个cookie信息是以sessionid为key,value为xxxx,cookie肯定会以响应的形式在响应头中出现。
第二次以及之后的请求:
③ 都会携带cookie信息,特别是sessionid。
如果换了浏览器,还能获取到session信息吗?
解:不可以,因为session依赖于cookie,换了浏览器,都没有cookie信息了。
文章来源于公众号【Python野路子】
相关推荐
- 软件测试|MySQL CROSS JOIN:交叉连接的详细解析
-
简介在MySQL数据库中,CROSSJOIN是一种用于生成两个或多个表的笛卡尔积的连接方法。CROSSJOIN不需要任何连接条件,它将左表的每一行与右表的每一行进行组合,从而生成一个包含所...
- 「MySQL笔记」left join-on-and 与 left join-on-where 的区别
-
1.摘要关于这两种写法的重要知识点摘要如下:left-join时,即使有相同的查询条件,二者的查询结果集也不同,原因是优先级导致的,on的优先级比where高on-and是进行韦恩运算连接...
- MySQL中的JOIN——联合查询的基本语法
-
MySQL中的JOIN指令用来将两个或多个表中的数据进行联合查询,根据连接条件来匹配记录,从而得到需要的结果集。在MySQL中,常见的JOIN类型包括INNERJOIN、LEFTJOIN和RIGH...
- MySQL 中的 CROSS JOIN:强大的连接工具
-
CROSSJOIN在MySQL里是一种挺特别的连接操作,它能弄出连接表的笛卡尔积。这就是说,要是表A有m行,表B有n行,那ACROSSJOINB的结果就会有m*n...
- 大厂必问:MySQL 三表 JOIN 操作的解析与性能优化,效率又如何?
-
大厂必问:MySQL三表JOIN操作的解析与性能优化策略,效率又如何?点击关注,开启技术之旅!大家好,这里是互联网技术学堂,无论你是一名程序员、设计师、还是对技术充满好奇心的普通人,都欢迎你加入...
- 面试题:MySQL 的 JOIN 查询优化(mysql查询优化方法)
-
MySQL的JOIN查询优化是提升数据库性能的关键环节。以下是综合多个技术文档的核心优化策略,按优先级和实现难度分类:一、索引优化:性能提升的基础为连接字段建立索引确保参与JOIN的列(通常...
- Flink中处理维表关联技术实现路径
-
在Flink中处理维表关联大体氛围TableSQLLookupJoin和DataStream算子函数,主要技术实现路径:I.FlinkSQL/TableAPI中的Lookup...
- 深入剖析Zookeeper原理(一)整体设计
-
1.ZK集群架构设计与特性1.ZK集群架构设计:ZK主要分为三种角色:Leader(领导者):一个Zookeeper集群同一时间只会有一个实际工作的Leader,它会发起并维护与各Follwer及...
- 多种负载均衡算法及其Java代码实现
-
首先给大家介绍下什么是负载均衡负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。负载均衡,英...
- 一分钟了解SpringCloud中的ribbon到底是什么,原理是啥?
-
1.概念ribbon是一款客户端负载均衡器,用于微服务之间的负载均衡。首先,什么是客户端负载均衡?如图,ribbon可以通过注册中心获取服务列表,然后自己执行自己的负载均衡策略来决定要访问哪个微服务,...
- Step by Step之腾讯云短信-验证码实践
-
在商城小程序和前端上线用了一阵子之后,用户提出了体验提升的需求,如忘记密码、绑定用户、快捷注册等,作为业界最佳实践的短信验证码登录、重置密码和注册等功能开发也就提上日程了,本文就以重置密码为例,将验证...
- 10分钟入门响应式:Springboot整合kafka实现reactive
-
Springboot引入Reactor已经有一段时间了,笔者潜伏在各种技术群里暗中观察发现,好像scala圈子的同仁们,似乎对响应式更热衷一点。也许是因为他们对fp理解的更深吧,所以领悟起来障碍性更少...
- 使用java随机生成有个性的用户名,LOL地名+水浒传,合计2808个
-
*随机生成用户名*取水浒传108好汉名字*取LOL地名26个,组合而成*一共可以生成2808个不同特色的用户名如果你在上网的时候,用户名难取的话,这里有很多可选择的用户名,现提供100个...
- 深入理解Math.random()的概率分布特性
-
直接上源码/***Returnsa{@codedouble}valuewithapositivesign,*返回一个带符号的double类型的数字,说人话就是返回一个非负...
- 编程英文 - 创建/生成/构建 (create/generate/build)
-
在软件开发中,create、generate和build这三个词经常被用到,它们都与"创造"或"产生"某些东西有关,但在具体使用场景和含义上有所不同。基本含义creat...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 软件测试|MySQL CROSS JOIN:交叉连接的详细解析
- 「MySQL笔记」left join-on-and 与 left join-on-where 的区别
- MySQL中的JOIN——联合查询的基本语法
- MySQL 中的 CROSS JOIN:强大的连接工具
- 大厂必问:MySQL 三表 JOIN 操作的解析与性能优化,效率又如何?
- 面试题:MySQL 的 JOIN 查询优化(mysql查询优化方法)
- Flink中处理维表关联技术实现路径
- 深入剖析Zookeeper原理(一)整体设计
- 多种负载均衡算法及其Java代码实现
- 一分钟了解SpringCloud中的ribbon到底是什么,原理是啥?
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)