P3 元宝第五单元笔记

第五单元 保持状态

5.1 Cookie

5.1.1 HTTP短连接是什么
短链接:不会记录之前的状态,当前通话结束就断开连接,不会记录状态,下次访问时要重新建立连接
长链接:当前通话结束后不会立马断开连接,在一定时间内再次连接会记录状态

在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web页中包含有其他的Web资源,如JavaScript文件、图像文件、CSS文件等;当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话。

但从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。

HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。

5.1.2 状态保持是什么

CookieSession一直以来都是Web开发中非常关键的一环,因为HTTP协议本身为无状态,每一次请求之间没有任何状态信息保持,往往我们的Web服务无法在客户端访问过程中得知用户的一些状态信息,比如是否登录等等;那么这里通过引入Cookie或者Seesion来解决这个问题

5.1.3 什么是COOKIE

当客户端访问时,服务端会为客户端生成一个Cookie键值对数据,通过Response响应给到客户端。当下一次客户端继续访问相同的服务端时,浏览器客户端就会将这个Cookie值连带发送到服务端

Cookie值存储在浏览器下,一般在你的浏览器安装目录的Cookie目录下,我们也可以通过F12或者各种浏览器的开发者工具来获取到

因为cookie是保存在浏览器中的一个纯明文字符串,所以一般来说服务端在生成cookie值时不建议存储敏感信息比如密码

5.1.4 框架对于COOKIE的操作

django的代码中,我们可以使用一些提供Response响应的类,如:HttpResponseredirect等实例的内置set_cookie函数来进行django项目中的Cookie设置

8
'''
key: Cookie的key值,未来通过该key值获取到对应设置好的Cookie。
value='': 对应Cookie的key值的value,比如: set_cookie(key='value',value='shuai')
max_age=None: Cookie生效的时间,单位为秒,如果Cookie值只持续在客户端浏览器的会话时长,那么这个值应该为None。存在该值时,expires会被计算得到。
expires=None: Cookie具体过期日期,是一个datetime.datetime对象,如果该值存在,那么max_age也会被计算得到
path='/': 指定哪些url可以访问到Cookie,默认/为所有。
domain=None: 当我们需要设置的为一个跨域的Cookie值,那么可以使用该参数,比如: domain='.test.com',那么这个Cookie值可以被www.test.com、bbs.test.com等主域名相同的域所读取,否则Cookie只被设置的它的域所读取。为None时,代表当前域名下全局生效。
secure=False: https加密传输设置,当使用https协议时,需要设置该值,同样的,如果设置该值为True,如果不是https连接情况下,不会发送该Cookie值。
httponly=False: HTTPOnly是包含在HTTP响应头部中Set-Cookie中的一个标记。为一个bool值,当设置为True时,代表阻止客户端的Javascript访问Cookie。这是一种降低客户端脚本访问受保护的Cookie数据风险的有效的办法
'''
import datetime
current_time = datetime.datetime.now() # 当前时间
expires_time = current_time + datetime.timedelta(seconds=10) # 向后推延十秒
set_cookie('key','value',expires=expires_time) #设置Cookie及对应超时时间
设置COOKIE

实现一下COOKIE的设置

from django.shortcuts import render,HttpResponse
# 设置cookie
resp.set_cookie("name","hahahhah")

以上视图函数返回一个HttpResponse对象,并在该对象中集成COOKIE值的设定,设置key值为testvalue值为hello cookie

获取COOKIE

再来简单的实现一下COOKIE的获取

# 获取cookie,cookie是保存在客户端的,获取要是有request请求
print(request.COOKIES.get("name"))

Cookie值存储在,request中的COOKIES属性中
并且该属性获取到的结果与python中的字典类似,直接通过内置函数get获取即可

删除COOKIE

这里通过该视图函数路由进行COOKIE的删除

# 删除cookie
resp.delete_cookie('name')

Cookie中删除指定的key及对应的value,如果key值不存在,也不会引发任何异常。
由于Cookie的工作方式,pathdomain应该与set_cookie时使用的值相同,否则Cookie值将不会被删除

通过response相应类的delete_cookie方法,本来应该在会话结束之后才消失的Cookie值,现在已经被直接删除掉。后台中通过Request中的Cookie字典获取到值也为None

不要忘记字典的get,获取不到结果时,返回None

但是,现在还有一个问题,我们在用户浏览器存储的Cookei值为明文,具有极大的安全隐患,django也提供了支持签证的Cookie值存储及获取方式


防止篡改COOKIE

通过set_signed_cookie函数进行持有签名的COOKIE值设置,避免用户在客户端进行修改

要记得,这个函数并不是对COOKIE值进行加密

HttpResonse.set_signed_cookie(key, value, salt='', max_age=None,
expires=None, path='/', domain=None, secure=None, httponly=True)
# 为cookie值添加签名,其余参数与set_cookie相同

Request.get_signed_cookie(key, salt='', max_age=None)
# 从用户请求中获取通过salt盐值加了签名的`Cookie`值

这里的salt要与之前存储时使用的salt值相同才可以解析出正确结果。
还要注意的是,如果对应的key值不存在,则会引发KeyError异常,所以要记得异常捕获来确定是否含有Cookie

def check_salt_cookie(request):
    try:
        salt_cookie = request.get_signed_cookie(key='salt_cookie',salt='nice')
    except KeyError: #获取不到该key值的Cookie
        response = HttpResponse('正在设置一个salt Cookie值')
        response.set_signed_cookie(key='salt_cookie',salt='nice',value='salt_cookie')
        return response
    else: #获取到了对应key值,展示到新的HttpResonse中
        return HttpResponse('获取到的salt Cookie值:%s' % salt_cookie)

第一次访问的时候,还没有加Cookie值,所以我们在获取的时候会抛出KeyError异常
此时捕获异常,并且设置Cookie即可;

再次刷新的时候,因为这里已经给出了Cookie值,则不会引发异常,会在页面中展示获取到的加盐Cookie

5.2 Session

5.2.1 Session的原理

虽然说有了Cookie之后,我们把一些信息保存在客户端浏览器中,可以保持用户在访问站点时的状态,但是也存在一定的安全隐患,Cookie值被曝露,Cookie值被他人篡改,等等。我们将换一种更健全的方式,也就是接下来要说的Session

Session在网络中,又称会话控制,简称会话。用以存储用户访问站点时所需的信息及配置属性。当用户在我们的Web服务中跳转时,存储在Session中的数据不会丢失,可以一直在整个会话过程中存活。

5.2.2 框架是如何存储管理Session的

django中,默认的Session存储在数据库中session表里。默认有效期为两个星期
session创建流程

  1. 客户端访问服务端,服务端为每一个客户端返回一个唯一的sessionid,比如xxx
  2. 客户端需要保持某些状态,比如维持登陆。那么服务端会构造一个{sessionid: xxx }类似这样的字典数据加到Cookie中发送给用户。注意此时,只是一个随机字符串,返回给客户端的内容并不会像之前一样包含实际数据。
  3. 服务端在后台把返回给客户端的xxx字符串作为key值,对应需要保存的服务端数据为一个新的字典,存储在服务器上,例如:{xxx : {id:1}}

之后的一些客户端数据获取,都是通过获取客户端向服务端发起的HttpRequest请求中里Cookie中的sessionid之后,再用该sessionid从服务端的Session数据中调取该客户端存储的Session数据

  • 注意:补充说明,默认存储在数据库的Session数据,是通过base64编码的,我们可以通过Pythonbase64模块下的b64decode()解码得到原始数据

整个过程结束之后:客户端浏览器存储的其实也只是一个识别会话的随机字符串(xxx)

而服务器中是通过这个随机的字符串(xxx:value)进行真正的存储

Session的使用必须在Settings配置下

INSTALLED_APPS = (
    ...
    'django.contrib.sessions',
    ...
)
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    ...
)
5.2.3 框架如何操作Session

settings.pySessionMiddleware激活后
在视图函数的参数request接收到的客户端发来的HttpResquest请求对象中都会含有一个session属性

这个属性和之前所讨论的Cookie类似,是一个类字典对象,首先支持如下常用字典内置属性

获取Session
session_data = request.session.get(Key)
session_data = request.session[Key]

Session中获取对应值,get方法获取时,如不存在该Key值,不会引发异常,返回None
而第二种直接通过get方法获取,如Key值不存在,引发KeyErro

删除Session
del request.seesion[Key]
# 删除对应session,Key值不存在时,引发KeyError

request.session.clear()
# 清空Session中的所有数据。这里客户端还会保留sessionid;
# 只不过在服务端sessionid对应的数据没有了
设置有效期
request.session.set_expiry(value)
# 设置Session的有效时间
'''
value: 有效时间。
    - 为整数时: 将在value为秒单位之后过期
    - 为0时: 将在用户关闭浏览器之后过期。
  - 为None时: 使用全局过期的设置,默认为两个星期,14天。
  - 为datetime时: 在这个指定时间后过期
'''

request.session.get_expiry_age()
# 返回距离过期还剩下的秒数

request.session.clear_expired()
# 清除过期的Session会话

  • session示例
class SessionOper(View):
    def get(self,request):
        # 设置session  使用request请求来设置session  会自动生成sessionid===
        request.session["name"] = "hahaha"
        # 获取session
        print(request.session.get("name"))
        # 删除session
        request.session.clear()
        # 获取session
        print(request.session.get("name"))
        return HttpResponse("OK")

用户在第一次访问时,会走else分支,此时还没有任何服务端的Session及客户端的Cookie值设定
那么我们会通过request.session[Key]的方式来设置一个Session值,值为TEST

当用户第二次访问时将展示出所设置好的Session值及在客户端浏览器中存储的sessionid


Session删除总结

使用的是del的针对性删除方式,这样不会将整个客户端的session删除掉

使用request.session.clear(),只是清空了服务端Session中的数据,但是客户端的Cookie中还会保存sessionid,只不过这个值对应的字符串所对应的用户数据是一个空

使用request.session.flush(),那么客户端Cookie中保存的sessionid首先会被删除,其次服务端通过sessionid值保存的用户数据也会被全部删除。

5.2.4 Session与Cookie的对比

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5.3 Csrf

5.3.1 什么是CSRF

CSRF(Cross-site request forgery):跨站请求伪造。

5.3.2 CSRF攻击模拟实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Awh3y0K2-1650974466652)(images/941968-20190422203527996-279231194.jpg)]

用户是网站 A 的注册用户,且登录进去,于是网站 A 就给用户下发 cookie。

从上图可以看出,要完成一次 CSRF 攻击,受害者必须满足两个必要的条件:

(1)登录受信任网站 A,并在本地生成 Cookie。(如果用户没有登录网站 A,那么网站 B 在诱导的时候,请求网站 A 的 api 接口时,会提示你登录)

(2)在不登出 A 的情况下,访问危险网站 B(其实是利用了网站 A 的漏洞)。

我们在讲 CSRF 时,一定要把上面的两点说清楚。

温馨提示一下,cookie 保证了用户可以处于登录状态,但网站 B 其实拿不到 cookie。

5.3.3 如何防止CSRF攻击

讨论 GET 和 POST 两种请求,对于 GET,其实也没什么需要防范的。为什么?

因为 GET 在 “约定” 当中,被认为是查询操作,查询的意思就是,你查一次,查两次,无数次,结果都不会改变(用户得到的数据可能会变),这不会对数据库造成任何影响,所以不需要加其他额外的参数。

所以这里要提醒各位的是,尽量遵从这些约定,不要在 GET 请求中出现 /delete, /update, /edit 这种单词。把 “写” 操作放到 POST 中。

对于 POST,服务端在创建表单的时候可以加一个隐藏字段,也是通过某种加密算法得到的。在处理请求时,验证这个字段是否合法,如果合法就继续处理,否则就认为是恶意操作。

<form method="post" action="/delete">
  <!-- 其他字段 -->
  <input type="hidden" />
</form>

这个 html 片段由服务端生成。

这的确是一个很好的防范措施,再增加一些处理的话,还能防止表单重复提交。

可是对于一些新兴网站,很多都采用了 “前后端分离开发” 的设计,或者退一步,无论是不是前后端分离,它的 HTML 可能是由 JavaScript 拼接而成,并且表单也都是异步提交。所以这个办法有它的应用场景,也有局限性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值