Django路由与会话深度探索:静态、动态路由分发,以及Cookie与Session的奥秘

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、静态路由
    • [1.1 静态路由匹配方式:](#1.1 静态路由匹配方式:)
    • [1.2 静态路由示例](#1.2 静态路由示例)
  • 二、动态路由
    • [2.1 动态路由参数传递](#2.1 动态路由参数传递)
    • [2.2 动态路由转换器](#2.2 动态路由转换器)
    • [2.3 动态路由传参与GET传参的异同点](#2.3 动态路由传参与GET传参的异同点)
  • 三、路由分发
    • [3.1 路由分发的概念](#3.1 路由分发的概念)
    • [3.2 include路由分发实现](#3.2 include路由分发实现)
  • 四、Cookie
    • [4.1 HTTP短连接是什么](#4.1 HTTP短连接是什么)
    • [4.2 状态保持是什么](#4.2 状态保持是什么)
    • [4.3 什么是COOKIE](#4.3 什么是COOKIE)
    • [4.4 框架对于COOKIE的操作](#4.4 框架对于COOKIE的操作)
  • 五、Session
    • [5.1 Session的原理](#5.1 Session的原理)
    • [5.2 框架是如何存储管理Session的](#5.2 框架是如何存储管理Session的)
    • [5.3 框架如何操作Session](#5.3 框架如何操作Session)
    • [5.4 Session示例](#5.4 Session示例)
    • [5.5 Session与Cookie的对比](#5.5 Session与Cookie的对比)
  • 六、CSRF
    • [6.1 什么是CSRF](#6.1 什么是CSRF)
    • [6.2 CSRF攻击模拟实现](#6.2 CSRF攻击模拟实现)
    • [6.3 如何防止CSRF攻击](#6.3 如何防止CSRF攻击)

前言

    主要介绍了Django中静态路由、动态路由、路由分发、Cookie和Session以及CSRF等内容


一、静态路由

Django的路由系统中,通过urls的匹配,可以直接映射到特定的视图函数或类视图,在当前整个路由中没有携带任何动态改变的值,称之为静态路由


通俗来说,访问路径中,不存在动态传递数据,称之为静态路由

urlpatterns属性:

urlpatterns是路由文件中的一个全局变量,用来存放路由及视图函数的映射关系

用户发起的请求URL都会首先进入主控制目录下的这个urls.py文件中进行查找匹配:

  1. 首先找到urls.py下的urlpatterns全局变量,这是一个路由规则实例的列表数据。
  2. 按照先后定义顺序,进行路由匹配。
  3. 找到第一个匹配项时停止匹配,执行匹配到的视图函数。
  4. 遍历完全,未发现匹配,django进行异常处理

其中urlpatterns中的每一个路由映射规则可以由pathre_path进行构造

1.1 静态路由匹配方式:

path方法:

python 复制代码
path(str, view, kwargs=None, name=None)
'''
str:一个匹配对应url地址的规则字符串
view:路由对应的视图函数,并且会自动封装HttpRequest作为第一个参数给这个视图函
kwargs:视图函数的关键字参数
name:该路由的全局命名,可以让我们方便的在django项目中任意部分显示的使用,相当于为url取变量名,接下来全局使用该命名值即可;当对应url路由改变之后,结合路由反向解析使用的地方不需要更改路由,
 案例:<a href="{% url url_name %}"></a>
'''

re_path方法:

python 复制代码
re_path(regex, view, kwargs=None, name=None)
'''
regex:一个匹配对应url地址的规则字符串
view:路由对应的视图函数,并且会自动封装HttpRequest作为第一个参数给这个视图函
kwargs:视图函数的关键字参数
name:该路由的全局命名,可以让我们方便的在django项目中任意部分显示的使用,相当于为url取变量名,接下来全局使用该命名值即可;当对应url路由改变之后,结合路由反向解析使用的地方不需要更改路由
'''

1.2 静态路由示例

python 复制代码
# views.py
from django.http import HttpResponse
def index(request):
    return HttpResponse('Hello Worlds!')   

# urls.py     
from django.urls import path,re_path
from urlapp import views
urlpatterns = [
    path('index/',views.index),
    re_path(r"^/$",views.index),#此处完善对应的正则表达式即可
    re_path(r"^index/\d+/$",IndexView.as_view()), #CBV
]

二、动态路由

动态路由:访问路径中,存在动态传递数据,称之为动态路由

2.1 动态路由参数传递

例如在遇到一些内容翻页的场景时,我们的连接可能是:xx.com/airticle_list/1/xx.com/airticle_list/2/

python 复制代码
# views.py
def index(request,x,y):
    content = "x:%s\ny:%s" % (x,y)  
    return HttpResponse(content)

定义如上函数,将会接收连接中的后两部份path值作为参数,分别依次给到xy

2.2 动态路由转换器

python 复制代码
# urls.py
from django.urls import path,re_path
from urlapp import views
urlpatterns = [
    path('<int:x>/<str:y>/',views.index), # 指明类型 访问:`http://127.0.0.1:8000/1/abc/`
    path("<x>/<y>/",views.index), # 不指明类型 访问:`http://127.0.0.1:8000/abc/abc/`
    
    re_path(r"^(?P<x>\d+)/(?P<y>[a-zA-Z]+)/$"), # (?P<name>pattern) 正则分组
    re_path(r"^(\d+)/([a-zA-Z]+)/$"),
]
  • 其他内置Path转换器,可以将我们的路由参数规定为指定类型
python 复制代码
'''
str:匹配除了路径分隔符(`/`)之外的非空字符串,这是默认的形式
int:匹配正整数,包含0
slug:匹配字母、数字以及横杠、下划线组成的字符串
uuid:匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00
path:匹配任何非空字符串,包含了路径分隔符
'''

自定义转换器:

在Django中,自定义路由转换器(Custom Route Converter) 允许你根据特定的正则表达式和规则来定义如何解析URL中的参数,并将其传递给视图函数或类视图。这种机制允许你更加灵活地处理URL模式,尤其是当内置的路由转换器(如intslugpath等)无法满足你的需求时。

python 复制代码
# urls.py
from django.urls import path,re_path,converters
class PhoneConverter:
    regex = '1[3-9]\d{9}'

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return str(value)
    
#在 项目的 urls.py 文件中 注册 自定义的路由转换器
converters.register_converter(PhoneConverter, 'phone')
#在路由配置中
urlpatterns = [
	path('index/<phone:id>/',views.index), # 指明类型
]

2.3 动态路由传参与GET传参的异同点

  • 动态路由传参,参数需要参与路由匹配,在路由匹配中获取参数
  • GET参数,参数部分不需要参与路由匹配,在视图中获取参数

三、路由分发

3.1 路由分发的概念

每个子app都拥有自己独立的urls.py路由映射文件,而主控路由文件里只需要使用include函数导入子app下路由文件即可,这就是路由分发

3.2 include路由分发实现

python 复制代码
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('urlapp/',include("urlapp.urls"))  # 使用include 实现路由分发,找到子app下的路由文件
]

四、Cookie

4.1 HTTP短连接是什么

HTTP短连接,也称为非持久连接,在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。


HTTP长连接,也称为持久连接(Persistent Connection)或HTTP keep-alive连接,是一种使用单个TCP连接来发送和接收多个HTTP请求/响应的通信方式。HTTP/1.1起,默认使用长连接。与HTTP短连接不同,长连接在发送一个HTTP请求后不会立即关闭TCP连接,而是保持连接打开状态,以便在同一连接上发送和接收后续的HTTP请求/响应。

4.2 状态保持是什么

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

客户端存储信息使用:Cookie

服务端存储信息使用:Session

4.3 什么是COOKIE

当客户端访问时,服务端会为客户端生成一个Cookie键值对数据,通过Response响应给到客户端。当下一次客户端继续访问相同的服务端时,浏览器客户端就会将这个Cookie值连带发送到服务端.
Cookie 是基于客户端的,它们与用户的浏览器会话相关联。服务器使用Cookie来跟踪和识别用户的浏览器会话,以便为用户提供个性化的体验或记住用户的设置和偏好。

Cookie 主要由以下几部分组成:

  1. 名称(Name):Cookie 的名称,用于标识该 Cookie
  2. 值(Value):与 Cookie 名称关联的值,通常用于存储用户信息或状态。
  3. 过期时间(Expires/Max-Age):指定 Cookie 的有效期。如果设置了过期时间,则 Cookie 会在该时间后自动失效;否则,Cookie 将在浏览器关闭时失效。
  4. 路径(Path):指定 Cookie 适用的页面路径。只有该路径下的页面才能访问该 Cookie。
  5. 域(Domain):指定可以接收该 Cookie 的域名。通常设置为设置该 Cookie 的网站的域名。
  6. 安全标志(Secure):如果设置了该标志,则只能通过 HTTPS 协议传输该 Cookie。这有助于增加 Cookie 的安全性。
  7. HttpOnly 标志:如果设置了该标志,则 JavaScript 无法访问该 Cookie。这有助于防止跨站脚本攻击(XSS)。

4.4 框架对于COOKIE的操作

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

python 复制代码
set_cookie(key, value='', max_age=None, expires=None, path='/',domain=None, secure=False, httponly=False)
'''
参数解释:
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也会被计算得到
如果同时设置了expires和max_age,老版本中以expires为准,新版本中会报错,建议使用:单独使用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数据风险的有效的办法。
'''

设置COOKIE:

python 复制代码
# views.py
from django.shortcuts import render,HttpResponse
# Create your views here.

def set_cookie(request):
    # 在HTTPResponse部分设置COOKIE值
    cookie_reponse = HttpResponse('这是一个关于cookie的测试')
    cookie_reponse.set_cookie('test','hello cookie')
    return cookie_reponse

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

设置过期时间:

bash 复制代码
import datetime
current_time = datetime.datetime.now() # 当前时间
expires_time = current_time + datetime.timedelta(seconds=10) # 向后推延十秒
set_cookie('key','value',expires=expires_time) #设置Cookie及对应超时时间

datetime.timedelta是Python中一个用于表示时间差的类。它可以表示一段时间,例如几天、几小时、几分钟等。使用datetime.timedelta可以在日期和时间上进行简单的加减操作。

获取COOKIE:

python 复制代码
def get_cookie(request):
    # 获取cookie值,从request属性中的COOKIE属性中
    cookie_data = request.COOKIES.get('test')
    return HttpResponse('Cookie值为:%s' % cookie_data)

Cookie值存储在request中的COOKIES属性中

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

删除COOKIE:

python 复制代码
def delete_cookie(request):
    response = HttpResponseRedirect('/check_cookie/')
    response.delete_cookie('test')
    return response

Cookie中删除指定的key及对应的value,如果key值不存在,也不会引发任何异常。

防止篡改COOKIE:

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

python 复制代码
HttpResonse.set_signed_cookie(key, value, salt='', max_age=None,
expires=None, path='/', domain=None, secure=None, httponly=True)
# 为cookie值添加签名,其余参数与set_cookie相同
python 复制代码
Request.get_signed_cookie(key, salt='', max_age=None)
# 从用户请求中获取通过salt盐值加了签名的`Cookie`值

这里的salt要与之前存储时使用的salt值相同才可以解析出正确结果。

还要注意的是,如果对应的key值不存在,则会引发KeyError异常,所以要记得异常捕获来确定是否含有Cookie

python 复制代码
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)

五、Session

5.1 Session的原理

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

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

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

5.3 框架如何操作Session

settings.pySessionMiddleware激活后

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

存储session:

python 复制代码
request.session[key] = value
request.session['name'] = 'zs'

获取Session:

python 复制代码
session_data = request.session.get(Key)
session_data = request.session[Key]

Session中获取对应值,get方法获取时,如不存在该Key值,不会引发异常,返回None;

而第二种直接通过get方法获取,如Key值不存在,引发KeyError

删除Session:

python 复制代码
del request.seesion[Key]
# 删除对应session,Key值不存在时,引发KeyError

request.session.clear()
# 清空Session中的所有数据。这里客户端还会保留sessionid;
# 只不过在服务端sessionid对应的数据没有了

request.session.flush()
# 直接删除当前客户端的的Seesion数据。这里不光服务端sessionid对应的数据没有了,客户端的`sessionid`也会被删除

设置有效期:

python 复制代码
request.session.set_expiry(value)
# 设置Session的有效时间
'''
value: 有效时间。
    - 为整数时: 将在value为秒单位之后过期  
    - 为0时: 将在用户关闭浏览器之后过期。
  - 为None时: 使用全局过期的设置,默认为两个星期,14天。
  - 为datetime时: 在这个指定时间后过期    2023-8-10 12:00:00
'''
python 复制代码
request.session.get_expiry_age()
# 返回距离过期还剩下的秒数
python 复制代码
request.session.clear_expired()
# 清除过期的Session会话

5.4 Session示例

python 复制代码
from django.shortcuts import render,HttpResponse

import datetime

def set_session(request):
    if request.session.get('test_id'):
        session_data = request.session.get('test_id')# 用户拿到的的session随机字符串
        session_key = request.session.session_key # 获取客户端浏览器中的SessionID值
        session_expire = request.session.get_expiry_age()
        now = datetime.datetime.now()
        expire_time = now + datetime.timedelta(seconds=session_expire)
        response_body = '<div>SessionID : %s</div>' % session_key + \
                   '<div>Session : %s</div>' % session_data + \
                   '<div>ExpireTime : %s</div>' % expire_time
        return HttpResponse(response_body)
    else:
        request.session['test_id'] = 'TEST'
        request.session.set_expiry(None)
        return HttpResponse('已设置好Session')

用户在第一次访问时,会走else分支,此时还没有任何服务端的Session及客户端的Cookie值设定

那么我们会通过request.session[Key]的方式来设置一个Session值,值为TEST
当用户第二次访问时将展示出所设置好的Session值及在客户端浏览器中存储的sessionid


python 复制代码
def delete_session(request):
    if request.session.get('test_id'):
        del request.session['test_id']
        return HttpResponse('Session被删了')
    else:
        return HttpResponse('目前没有任何需要删除的session')

5.5 Session与Cookie的对比

1.cookie数据存放在客户的浏览器上,session数据放在服务器上。

  1. cookie不是很安全 ,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。

  2. session会在一定时间内保存在服务器上 。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。

  3. 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

六、CSRF

6.1 什么是CSRF

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

6.2 CSRF攻击模拟实现

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

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

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

6.3 如何防止CSRF攻击

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

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

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

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

html 复制代码
<form method="post" action="/delete">
  <!-- 其他字段 -->
  <input type="hidden" />
</form>

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

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

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

相关推荐
笨鸟先飞,勤能补拙19 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能20 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂20 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study21 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境1 天前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境1 天前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1231 天前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102161 天前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb001 天前
一个项目带你入门AI应用开发01
python
0566461 天前
Python康复训练——常用标准库
开发语言·python·学习