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

本文涉及的产品
.cn 域名,1个 12个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Django路由与会话深度探索:静态、动态路由分发,以及Cookie与Session的奥秘

前言

    主要介绍了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方法:

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

re_path方法:

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

1.2 静态路由示例

# 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/

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

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

2.2 动态路由转换器

# 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转换器,可以将我们的路由参数规定为指定类型
'''
str:匹配除了路径分隔符(`/`)之外的非空字符串,这是默认的形式
int:匹配正整数,包含0
slug:匹配字母、数字以及横杠、下划线组成的字符串
uuid:匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00
path:匹配任何非空字符串,包含了路径分隔符
'''

自定义转换器:

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

# 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路由分发实现

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设置

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:

# 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

设置过期时间:

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:

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:

def delete_cookie(request):
    response = HttpResponseRedirect('/check_cookie/')
    response.delete_cookie('test')
    return response

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

防止篡改COOKIE:

通过set_signed_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)

五、Session

5.1 Session的原理

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

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

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

5.3 框架如何操作Session

settings.pySessionMiddleware激活后

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

存储session:

request.session[key] = value
request.session['name'] = 'zs'

获取Session:

session_data = request.session.get(Key)
session_data = request.session[Key]

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

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

删除Session:

del request.seesion[Key]
# 删除对应session,Key值不存在时,引发KeyError
request.session.clear()
# 清空Session中的所有数据。这里客户端还会保留sessionid;
# 只不过在服务端sessionid对应的数据没有了
request.session.flush()
# 直接删除当前客户端的的Seesion数据。这里不光服务端sessionid对应的数据没有了,客户端的`sessionid`也会被删除

设置有效期:

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

5.4 Session示例

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


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数据放在服务器上。

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

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

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

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

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

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

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

相关文章
|
1月前
|
Python
Django 框架的路由系统
Django 框架的路由系统
42 6
|
4月前
|
存储 缓存 中间件
Django 框架中 Session 的用法
【8月更文挑战第30天】
40 6
|
3月前
|
Python
django 路由分发:原理
django 路由分发:原理
|
4月前
|
缓存 数据库 数据安全/隐私保护
Django入门到放弃之session
Django入门到放弃之session
|
4月前
|
Python SEO
Django入门到放弃之路由
Django入门到放弃之路由
|
4月前
|
存储 缓存 数据管理
Django后端架构开发:后台管理与会话技术详解
Django后端架构开发:后台管理与会话技术详解
62 0
|
4月前
|
Python
[django]路由变量与正则表达式
[django]路由变量与正则表达式
|
6月前
|
安全 API Python
Django 如何使用视图动态输出 CSV 以及 PDF
这一篇我们需要用到 python 的 csv 和 reportLab 库,通过django视图来定义输出我们需要的 csv 或者 pdf 文件。
|
6月前
|
JSON API 网络架构
Django REST framework视图集与路由详解:深入理解ViewSet、ModelViewSet与路由映射器
Django REST framework视图集与路由详解:深入理解ViewSet、ModelViewSet与路由映射器
|
1月前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
61 4