前言
主要介绍了Django中静态路由、动态路由、路由分发、Cookie和Session以及CSRF等内容
一、静态路由
Django的路由系统中,通过urls的匹配,可以直接映射到特定的视图函数或类视图,在当前整个路由中没有携带任何动态改变的值,称之为静态路由。
通俗来说,访问路径中,不存在动态传递数据,称之为静态路由
urlpatterns属性:
urlpatterns
是路由文件中的一个全局变量,用来存放路由及视图函数的映射关系
用户发起的请求URL
都会首先进入主控制目录下的这个urls.py
文件中进行查找匹配:
- 首先找到
urls.py
下的urlpatterns
全局变量,这是一个路由规则实例的列表数据。 - 按照先后定义顺序,进行路由匹配。
- 找到第一个匹配项时停止匹配,执行匹配到的视图函数。
- 遍历完全,未发现匹配,
django
进行异常处理
其中urlpatterns
中的每一个路由映射规则可以由path
或re_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
值作为参数,分别依次给到x
和y
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模式,尤其是当内置的路由转换器(如
int
、slug
、path
等)无法满足你的需求时。
# 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 状态保持是什么
Cookie
及Session
一直以来都是Web开发中非常关键的一环,因为HTTP
协议本身为无状态,每一次请求之间没有任何状态信息保持,往往我们的Web服务无法在客户端访问过程中得知用户的一些状态信息,比如是否登录等等;那么这里通过引入Cookie
或者Seesion
来解决这个问题
客户端存储信息使用:Cookie
服务端存储信息使用:Session
4.3 什么是COOKIE
当客户端访问时,服务端会为客户端生成一个
Cookie
键值对数据,通过Response
响应给到客户端。当下一次客户端继续访问相同的服务端时,浏览器客户端就会将这个Cookie
值连带发送到服务端.
Cookie
是基于客户端的,它们与用户的浏览器会话相关联。服务器使用Cookie
来跟踪和识别用户的浏览器会话,以便为用户提供个性化的体验或记住用户的设置和偏好。
Cookie 主要由以下几部分组成:
- 名称(Name):Cookie 的名称,用于标识该 Cookie
- 值(Value):与 Cookie 名称关联的值,通常用于存储用户信息或状态。
- 过期时间(Expires/Max-Age):指定 Cookie 的有效期。如果设置了过期时间,则 Cookie 会在该时间后自动失效;否则,Cookie 将在浏览器关闭时失效。
- 路径(Path):指定 Cookie 适用的页面路径。只有该路径下的页面才能访问该 Cookie。
- 域(Domain):指定可以接收该 Cookie 的域名。通常设置为设置该 Cookie 的网站的域名。
- 安全标志(Secure):如果设置了该标志,则只能通过 HTTPS 协议传输该 Cookie。这有助于增加 Cookie 的安全性。
- HttpOnly 标志:如果设置了该标志,则 JavaScript 无法访问该 Cookie。这有助于防止跨站脚本攻击(XSS)。
4.4 框架对于COOKIE的操作
在
django
的代码中,我们可以使用一些提供Response
响应的类,如:HttpResponse
,redirect
等实例的内置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
值为test
,value
值为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.py
中SessionMiddleware
激活后在视图函数的参数
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 拼接而成,并且表单也都是异步提交。所以这个办法有它的应用场景,也有局限性。