雅安交通建设集团网站,自己做一网站_多做宣传.,广州网站建设学习,广州 网站制作本节主要目录如下#xff1a;
一、请求响应循环
二、HTTP请求
2.1、请求报文
2.2、Request对象
2.3、在Flask中处理请求
2.4、请求钩子
三、HTTP响应
3.1、响应报文
3.2、在Flask中生成响应
3.3、响应格式
3.4、Cookie
3.5、session#xff1a;安全的Cookie
四、… 本节主要目录如下
一、请求响应循环
二、HTTP请求
2.1、请求报文
2.2、Request对象
2.3、在Flask中处理请求
2.4、请求钩子
三、HTTP响应
3.1、响应报文
3.2、在Flask中生成响应
3.3、响应格式
3.4、Cookie
3.5、session安全的Cookie
四、Flask上下文
4.1、上下文全局变量
4.2、激活上下文*
4.3、上下文钩子
五、HTTP进阶实践
5.1、重定向回上一个页面
5.2、使用AJAX技术发送异步请求
5.3、HTTP服务器端推送
5.4、Web安全防范 一、请求响应循环
“请求-响应循环”客户端发出请求服务器处理请求并返回响应。 Flask Web程序的工作流程
当用户访问一个URL浏览器便生成对应的HTTP请求经由互联网发送到对应的Web服务器。Web服务器接收请求通过WSGI将HTTP格式的请求数据转换为成我们的Flask程序能够使用的Python数据。
在程序中Flask根据请求的URL执行对应的视图函数获取返回值生成响应。响应依此经过WSGI转换生成HTTP响应再经由Web服务器传递最终被发出请求的客户端接收。浏览器渲染响应中包含的HTML和CSS代码并执行JavaScript代码最终把解析后的页面呈现在用户浏览器的窗口中。
二、HTTP请求
一个标准的URL由很多部分组成以下面这个URL为例 http://helloflask.com/hello?nameGrey URL组成部分
信息说明http://协议字符串指定要使用的协议helloflask.com服务器的地址域名/hello?nameGrey要获取的资源路径path类似UNIX的文件目录结构
2.1、请求报文
请求的实质是发送到服务器上的一些数据这种浏览器与服务器之间交互的数据被称为报文请求时浏览器发送的数据被称为请求报文而服务器返回的数据被称为响应报文。
请求的报文由请求的方法、URL、协议版本、首部字段以及内容实体组成。
请求报文示意表:
组成说明请求报文内容报文首部请求行方法、URL、协议GET/hello HTTP/1.1报文首部各种首部字段Host:helloflask.com Connection:keep-alive Cache-Control:max-age0 User-Agent:...空行报文主体nameGrey
常见的HTTP方法
方法说明GET获取资源POST传输数据PUT传输文件DELETE删除资源HEAD获得报文首部OPTIONS询问支持的方法
2.2、Request对象
Flask的请求对象requests封装了从客户端发来的请求报文当收到请求后请求对象会提供多个属性来获取URLhttp://helloflask.com/hello?nameGrey的各个部分
属性值属性值pathu/hellobase_urluhttp://helloflask.com/hellofull_pathu/hello?nameGreyurluhttp://helloflask.com/hello?nameGreyhostu/helloflask.comurl_rootuHelloFlaskhost_urlu/HelloFlask
除了URL请求报文中的其他信息都可以通过request对象提供的属性和方法获取
属性/方法说明args存储解析后的查询字符串可通过字典方式获取键值。valueWerkze CombinedMultiDict 对象结合了 args 和form 属性的值headers一个 Werkzeug EnvironHeaders 象包含首部字段 可以以字典的形式操作user_agent用户代理 User Agent,UA包含了用户的客户端类型操作系统类型等信息 from flask import Flaskapp Flask(__name__)app.route(/hello)def hello():name request.args.get(name,Flask) # 获取查询参数name的值return h1hello,%s/h1 % name
2.3、在Flask中处理请求
URL是指向网络上资源的地址。在Flask中我们需要让请求的URL匹配对应的视图函数视图函数返回值就是URL对应的资源。
2.3.1、路由匹配
为了便于将请求分发到对应的视图函数程序实例中存储了一个路由表app.url_map其中定义了URL规则和视图函数的映射关系。
当请求的URL与某个视图函数的URL规则匹配成功时对应的视图函数就会被调用。使用flask routes命令可以查看程序中定义的所有路由这个列表由app.url_map解析得到 $ flask routesEndpoint Methods Rule ----------- ------- -----------------------greet GET /greet/namegreet GET /greethello_world GET /static GET /static/path:filename
在输出的文本中我们可以看到每个路由对应的端点、HTTP方法和URL规则其中static端点是Flask添加的特殊路由用来访问静态文件。
2.3.2、设置监听的HTTP方法
在app.route()装饰器中使用methods参数传入一个包含监听的HTTP方法的可迭代对象。比如下面的视图函数同时监听GET和POST请求 app.route(/hello,methods[GET,POST])def hello():return h1Hello, Flask!/h1
当某个请求的方法不合符要求时请求将无法被正常处理。返回405错误响应表示请求方法不允许。
2.3.3、URL处理
Flask内置的URL变量转换器
转换器说明string不包含斜线的字符串默认值int整型float浮点数path包含斜线的字符串。static路由的URL规则中的filename变量就使用了这个转换器any匹配一系列给定值中的一个元素uuidUUID字符串
转换器通过特定的规则指定即“转换器变量名”。int:year把year的值转换为整数因此我们可以在视图函数中直接对year变量进行数学计算 app.route(goback/int:year)def go_back(year):return pWelcome to %d/p % (2018 - year)
在用法上唯一特别的是any转换器需要在转换器后添加括号来给出可选值“any(value1,value2,...):变量名”比如
app.route(/colors/any(blue,white,red):color)def three_colors(color):return pLove is patient and kind,Love is not jealous or boastful or proud or rude./p
还可以在any转换器中传入一个预先定义的列表可通过格式化字符串的方式使用%或是format()函数来构建URL规则字符串 colors [blue,white,red]app.route(/colors/any(%S):color %s str(colors)[1:-1])...
2.4、请求钩子
有时候需要对请求进行预处理和后处理这时可以使用Flask提供的一些请求钩子它们可以用来注册在请求处理的不同阶段执行的处理函数或称为回调函数即Callback。
这些请求钩子使用装饰器实现通过程序实例app调用以before_request钩子请求之前为例当对一个函数附加了app.before_request装饰器后就会将这个函数注册为before_request处理函数每次执行请求前都会触发所有before_request处理函数。
Flask默认实现的五种请求钩子
钩子说明before_first_request注册一个函数在处理第一个请求前运行before_request注册一个函数在处理每个请求前运行after_request注册一个函数如果没有未处理的异常抛出会在每个请求结束后运行teardown_request注册一个函数即使有未处理的异常抛出会在每个请求结束后运行。如果发生异常会传入异常对象作为参数到注册的函数中after_this_request在视图内注册一个函数会在这个请求结束后运行
使用和app.route()装饰器基本相同每个钩子可以注册任意多个处理函数函数名并不是必须和钩子函数名称相同示例 app.before_requestdef do_something()pass # 这里的代码会在每个请求处理前执行
使用情况示例
before_first_request在完整程序中运行程序前我们需要进行一些程序的初始化操作比如创建数据库表添加管理员用户。before_request网站上要记录用户最后在线时间可以通过用户最后发送请求时间来实现。 after_request在视图函数中进行数据库操作比如更新、插入等之后需要将更改提交到数据库中。
另一种常见的应用是建立数据库连接通常会有多个视图函数需要建立和关闭数据库连接这些操作基本相同。一个理想的方法是在强求之前before_request建立连接在请求之后teardown_request关闭连接。
三、HTTP响应
在Flask程序中客户端发出的请求触发相应的视图函数获取返回值会作为响应的主体最后生成完整的响应即响应报文。
3.1、响应报文
响应报文主要由协议版本、状态码、原因短语、响应首部和响应主体组成。以向localhost:5000/hello的请求为例服务器生成的响应报文示意
组成说明响应报文内容报文首部状态行协议、状态码、原因短语HTTP/1.1 200 OK报文首部各种首部字段Content-Type:text/html;charsetutf-8 ...空行报文主体h1Hello Human!/h1
常见状态码和相应的原因短语
类型状态码原因短语说明成功200OK请求被正常处理201Created请求被处理并创建了一个新资源204No Content请求处理成功但无内容返回重定向301Moved Permanently永久重定向302Found临时性重定向304Not Modified请求的资源未被修改重定向到缓存的资源客户端错误400Bad Request表示请求无效即请求报文中存在错误401Unauthorized类似403表示请求的资源需要获取授权信息在浏览器会弹出认证弹窗403Forbidden表示请求的资源被服务器拒绝访问404Not Found表示服务器上无法找到请求的资源或URL无效服务器端错误500Internal Server Error服务器内部发生错误
3.2、在Flask中生成响应
响应在Flask中用Response对象表示大部分情况我们只负责返回主体内容。
视图函数可以返回最多由三个元素组成的元组响应主体、状态码、首部字段可以为字典或是两元素元组组成的列表。 # 普通的响应可以只包含主体app.route(/hello)def hello():...return h1Hello,Flask!/h1# 默认状态码为200下面指定不同的状态码app.route(/hello)def hello():...return h1Hello,Flask!/h1,201# 要生成状态码为3XX的重定向响应app.route(/hello)def hello():...return ,302{Location,http://www.example.com}
3.2.1、重定向
当某个用户在没有经过认证的情况下访问需要登录后才能访问的资源程序通常会重定向到登录页面。
除了上一节手动生成302响应我们可以使用Flask提供的redirect()函数来生成重定向响应重定向的目标URL作为第一个参数 from flask import Flask,redirect# ...app.route(/hello)def hello():return redirect(http://www.example.com)# 使用redirect()函数时默认的状态码为302即临时重定向。若要修改则在函数中第二个参数
若要在程序内重定向到其他视图只需要在redirect()函数中使用url_for()函数生成目标URL即可 # http/app.py重定向到其他视图from flask import Flask,url_for...app.route(/hi)def hi():...return redirect(url_for(/hello)) # 重定向到/helloapp.route(/hello)def hello():...
3.2.2、错误响应
使用Flask提供的abort()函数手动返回错误响应在abort()函数中传入状态码即可返回对应的错误响应 from flask import Flask,abort..app.route(/404)def not_found():abort(404)
3.3、响应格式
Flask默认使用HTML格式返回响应在Content-Type字段中定义设置不同的MIME类型以返回不同的响应数据格式。以默认的HTML为例 Content-Type:text/html;charsetutf-8
若要使用其他MIME类型通过Flask提供的make_response()方法生成响应对象传入响应的主体作为参考然后使用响应对象的mimetype属性设置MIMW类型 from flask import make_responseapp.route(/foo)def foo():response make_response(Hello,World!)response.mimetype text/plainreturn response
常见的数据格式有纯文本、HTML、XML和JSON
3.3.1、纯文本
MIME类型text/plain # 示例Noteto:Peterfrom:Janeheading:Reminderbody:Dont forget the party!
3.3.2、HTML
MIME类型text/html # 示例!DOCTYPE htmlhtmlhead/headbodyh1Note/h1pto:Peter/ppfrom:Jane/ppheading:Reminder/ppbody:strongDont forget the party!/strong/p/body/html
3.3.3、XML
MIME类型application/xml
# 示例?xml version1.0 encodingUTF-8?note toPeter/to fromJane/from headingReminder/heading body Don’t forget the party!/body / note
XML一般作为AJAX请求的响应格式或是Web API的响应格式。
3.3.4、JSON
MIME类型application/json # 示例{note:{to:Peter,from:Jane,heading:Reminder,body:Dont forget the party!}}
可以直接从Flask中导入json对象然后调用dumps()方法将字典、列表或元组序列化为JSON字符串再使用前面介绍的方法修改MIME类型即可返回JSON响应例如 from flask import Flask,make_response,json...app.route(/foo)def foo():data {name:Grey Li,gender:male}response make_response(json.dumps(data))response.mimetype application/jsonreturn response
除此Flask提供更方便的jsonify()函数仅需要传入数据或参数它会对我们传入的参数进行序列化转化成JSON字符串作为响应的主体然后生成一个响应对象并且设置正确的MIME类型。 # 上述简化版jsonify()函数from flask import jsonifyapp.route(/foo)def foo():return jsonify({name:Grey Li,gender:male})# jsonify()函数默认生成200响应
3.4、Cookie
HTTP是无状态协议。就是说在一次请求响应结束后服务器不会留下任何关于对方状态的信息。
Cookie技术通过在请求和响应报文中添加Cookie数据来保存客户端的状态信息。
在Flask中使用Response类提供的set_cookie()方法在响应中添加一个cookie。使用方法先使用make_response()方法手动生成一个响应对象传入响应主体作为参数。这个响应对象默认实例化内置的Response类。
Response类的常用属性和方法
方法/属性说明headers一个Werkzeug的Headers对象表示响应首部可以像字典一样操作status状态码文本类型status_code状态码整型mimetypeMIME类型set_cookie()用来设置一个cookie
set_cookie() 方法支持多个参数来设置Cookie的选项
属性说明keycookie的键valuecookie的值max_agecookie被保存的时间数单位为秒默认在用户会话结束时过期expires具体的过期时间一个datetime对象或UNIX时间戳path限制cookie只在给定的路径可用默认为整个域名domain设置cookie可用的域名secure如果为True只有通过HTTPS才可以使用httponly如果为True进制客户端JavaScript获取cookie
set_cookei视图用来设置cookie它会将URL中的name变量的值设置到名为name的cookie里 from flask import Flask,make_response...app.route(/set/name)def set_cookie(name):response make_response(redirect(url_for(hello)))response.set_cookie(name,name)return response# 查看浏览器的Cookie会看到多了一块名为name的cookie# 在Flask中Cookie可以通过请求对象的cookies属性读取。在修改后的hello视图中如果没有从查询参数中获取到name的值就从cookie中寻找from flask import Flask,requestapp.route(/)app.route(/hello)def hello():name request.args.get(name)if name is None:name request.cookies.get(name,human) # 从Cookie中获取name值return h1Hello,%s/h1% name
3.5、session安全的Cookie
Flask提供session对象将Cookie数据加密存储。
附注在编程中session指用户会话又称对话即服务器和客户端/浏览器之间或桌面程序和用户之间的交互活动、在Flask中session对象用来加密Cookie。默认情况下它会把数据存储在浏览器上一个名为session的cookie里。
3.5.1、设置程序密钥
session通过密钥对数据进行签名以加密数据通过Flask.secret_key属性或配置变量SECRET_KEY设置比如 app.secret_key secret string# 更安全的做法是把密钥写进环境变量中或保存在.env文件值SECRET_KEYsecret string# 然后在程序脚本中使用os模块提供的getenv()方法获取import os# ...app.secre_key os.getenv(SECRET_KEY,secret string)# getenv()方法中的第二个参数作为没有获取到对应环境变量时使用的默认值。
3.5.2、模拟用户认证 # 使用session模拟用户的认证功能from flask import redirect,session,url_forapp.route(/login)def login():session[logged_in] True # 写入sessionreturn redirect(url_for(hello))
当支持用户登录后我们就可以根据用户的认证状态分别显示不同的内容。在login视图的最后我们将程序重定向到hello视图 from flask import request,sessionapp.route(/)app.route(/hello)def hello():name request.args.get(name,Human)response h1Hello,%s/h1% name# 根据用户认证状态返回不同的内容if logged_in in session:response [Authenticated]else:response [Not Authenticated]return response
程序中的某些资源仅提供给登入的用户比如管理后台这时我们就可以通过判断session是否存在logged_in键来判断用户是否认证 # 模拟管理后台from flask import session,abortapp.route(/admin)def admin():if logged_in not in session:abort(403)return Welcome to admin page.# 通过判断logged_in是否存在session中可以实现如果用户已经认证会返回一个提示文字否则返回403错误响应。
登出用户的logout视图实际操作就是把代表用户认证的logged_in cookie删除这通过session对象的pop方法实现 from flask import sessionapp.route(/logout)def logout():if logged_in in session:session.pop(logged_in)return redirect(url_for(hello))
提示默认session cookie会在用户关闭浏览器时删除。通过将session.permanent属性设置为True可以将session的有效期延长。Flask.permanent_session_lifetime属性值对应的datetime.timedelta对象也可以通过配置变量PERMANENT_SESSION_LIFETIME设置默认为31天。
注意加密仅能保证session的内容不被篡改借助工具仍可以读取因此不能在session中存储敏感信息比如用户密码。
四、Flask上下文
我们可以把编程中的上下文理解为当前环境的快照。Flask中有两种上下文程序上下文和请求上下文。
4.1、上下文全局变量
每一个视图都需要上下文信息。前面实例中直接从Flask导入一个全局的request对象然后在视图函数里直接调用request的属性获取数据。为了方便获取这两种上下文环境中存储的信息Flask提供了四个上下文全局变量
变量名上下文类别说明current_app程序上下文指向处理请求的当前程序实例g程序上下文替代Python的全局变量用法确保仅在当前请求中可用。用于存储全局数据每次请求都会重设request请求上下文封装客户端发出的请求报文数据session请求上下文用于记住请求间的数据通过前面的Cookie实现
在不同的视图函数中request对象都表示和视图函数对应的请求也就是当前请求。而程序也会有多个程序实例的情况为了能获取对应的程序实例而不是固定的某一个程序实例我们就需要current_app变量。
g存储在程序上下文中而程序上下文会随着每一个请求的进入而激活随着每一个请求的处理完毕而销毁所以每次请求都会重设这个值。通常结合钩子来保存每个请求处理前所需要的全局变量比如当前登入的用户对象数据库连接等。 from flask import gapp.before_requestdef get_name():g.name request.args.get(name)
设置这个函数后在其他视图中可以直接使用g.name获取对应的值。另外g 也支持使用类似字典的get()、pop以及setdefault()方法进行操作。
4.2、激活上下文*
Flask自动激活程序上下文的情况
使用flask run命令启动程序时旧方法app.run()方法启动程序时执行使用app.cli.command()装饰器注册的flask命令时使用flask shell命令启动Python Shell时
当请求进入时Flask会自动激活请求上下文程序上下文也自动激活这时我们可以使用request和session变量。请求处理完毕后两个上下文都自动销毁拥有相同的生命周期。
如果我们在没有激活上下文时使用这些变量Flask就会抛出RuntimeRrror异常 RuntimeError:Working outside of application context.或是RuntimeError:Working outside of request context.
手动激活上下文 # Python Shell# 程序上下文对象使用app.app_context()获取 from app import app from flask import current_app with app.app_context():... current_app.nameapp# 或是显式地使用push()方法推送激活上下文在执行完相关操作时使用pop()方法销毁上下文 from app import app from flask import current_app app_ctx app.app_context() app_ctx.push() current_app.nameapp app_ctx.pop()# 而请求上下文可以通过test_request_context()方法临时创建 from app import app from flask import request with app.test_request_context(/hello):... request.methodGET# 同样的这里也可以使用push()和pop()方法显式地推送和销毁请求上下文
4.3、上下文钩子
Flask为上下文提供了一个teardown_appcontext钩子使用它注册的回调函数会在程序上下文被销毁时调用而且通常也会在请求上下文被销毁时调用。 # 比如在每个请求处理结束后销毁数据库连接app.teardown_appcontextdef teardown_db(exception):...db.close()
五、HTTP进阶实践
5.1、重定向回上一个页面 # 创建两个视图函数foo和bar,分别显示一个Foo页面和一个Bar页面app.route(/foo)def foo():return h1Foo page/h1a href%sDo something/a % url_for(do_something)app.route(/bar)def bar():return h1Bar page/h1a href%sDo something/a % url_for(do_something)# 这两个页面都添加了一个指向do_something视图的链接app.route(/do_something)def do_something():return redirect(url_for(hello))
要完成的操作在Foo页面上单击链接我们希望被重定向回Foo页面Bar页面同理
5.1.1、获取上一个页面的URL
要重定向回上一个页面最关键的是获取上一个页面的URL。上一个页面的URL一般可以通过两种方式获取
1HTTP referer
HTTP referer是一个用来记录请求发起地址的HTTP首部字段即访问来源。当用户在某个站点单击链接浏览器向新链接所在的服务器发起请求请求的数据中包含HTTP_REFERER字段记录了用户所在原站点URL。
这个值通常用来追踪用户在Flask中referer的值可以通过请求对象的referrer属性获取即request.referrer。现在可改写do_something视图的返回值 return redirect(request.referrer)
但在多种情况下referrer字段会是空值比如在浏览器的地址栏输入URL或是用户出于保护隐私的考虑使用了防火墙软件等修改了referrer字段。我们需要加一个备选项 return redirect(request.referrer or url_for(hello))
2查询参数
在URL中手动加入包含当前页面URL的查询参数这个参数一般命名为next。
# 在foo和bar视图的返回值中的URL后添加next参数app.route(/foo)def foo():return h1Foo page/h1a href%sDo something/a % url_for(do_something,nextrequest.full_path)app.route(/bar)def bar():return h1Bar page/h1a href%sDo something/a % url_for(do_something,nextrequest.full_path)
在程序内部只需要使用相对URL所以这里使用request.full_path获取当前页面的完整路径。在do_something视图中我们获取这个next值然后重定向到对应的路径 return redirect(request.args.get(next))
为了避免next参数为空的情况添加备选项如果为空就重定向到hello视图 return redirect(request.args.get(next,url_for(hello)))
3整合
为了覆盖更全面我们将这两种方式搭配起来一起使用首先获取next参数如果为空就尝试获取referer如果仍为空就重定向到hello视图。因为在不同视图执行这部分操作的代码完全先那个塔可以创建一个通用的redirect_back()函数 # 重定向回上一个页面def redirect_back(defaulthello, **kwargs):for target in request.args.get(next), request.referrer:if target:return redirect(target)return redirect(url_for(default, **kwargs))# 在do_something视图中使用这个函数的示例app.route(/do_something_and_redirect)def do_something():return redirect_back()
5.1.2、对URL进行安全验证
鉴于referer和next容易被篡改的特性如果我们不对这些值进行验证则会形成开发重定向Open Redirect漏洞。如果我们不验证next变量指向的URL地址是否属于我们的应用内那么程序很容易就会被重定向到外部地址。 # 创建一个URL验证函数is_safe_url()用来验证next变量值是否属于程序内部URLfrom urllib.parse import urlparse, urljoindef is_safe_url(target):ref_url urlparse(request.host_url) # 获取程序内的主机URLtest_url urlparse(urljoin(request.host_url, target)) # 将目标URL转换为绝对URL使用urlparse()函数解析两个URLreturn test_url.scheme in (http, https) and ref_url.netloc test_url.netloc # 验证只有属于程序内部的URL才会被返回# 在执行重定向回上一个页面的redirect_back()函数中我们使用is_safe_url()验证next和referer的值def redirect_back(defaulthello, **kwargs):for target in request.args.get(next), request.referrer:if not target:continueif is_safe_url(target):return redirect(target)return redirect(url_for(default, **kwargs))
5.2、使用AJAX技术发送异步请求
5.2.1、AJAX
AJAX指异步Javascript和XML它不是编程语言或通信协议而是一些列技术的组合体。ajax让我们在不重载页面的情况下和服务器进行数据交换。加上JavaScript和DOM文档对象模型我们就可以在接收到数据后局部更新页面。XML指数据的交互模式也可以是纯文本、HTML或JSON。
使用AJAX加载数据的情况用户鼠标向下滚动到底部时在后台发送请求获取数据然后插入文章
以删除某个资源为例AJAX实现步骤
当单击“删除”按钮时客户端在后台发送一个异步请求页面不变在接收响应前可以进行其他操作。服务器端接收请求后执行删除操作返回提示消息或是无内容的 204 响应客户端接收到响应 使用 JavaScript 更新页面移除资源对应的页面元素
5.2.2、使用jQuery发送AJAX请求
jQuery 是流行的 JavaScript 库它包装了 JavaScript 。对于AJAX它提供了多个相关的方法使用它可以很方便地实现AJAX操作。
使用jQuery的ajax()函数发送AJAX请求。其所支持的参数
参数参数值类型及默认值说明url字符串默认为当前页地址请求的地址type字符串默认为“GET“请求的方式即HTTP方法比如GET、POST、DELETE等data字符串无默认值发送到服务器的数据。会被 jQuery 自动转换为查询字符串dataType字符串默认由jQuery自动判断期待服务器返回的数据类型可用的值如下“xml.html script”json jsonp””text”contentTypr字符串默认为‘application/x-www-form-urlencoded;charsetUTF-8发送请求时使用的内容类型即请求首部放Content-Type字段内容complete函数无默认值请求完成后调用的回调函数suceess函数无默认值请求成功后的调用的回调函数error函数无默认值请求失败后调用的回调函数
5.2.3、返回“局部数据”
对于处理AJAX请求的视图函数来说不会返回完整的HTM响应而是局部数据常见三种类型
1、纯文本或局部HTML模板
纯文本可以在JavaScript用来直接替换页面中的文本值而局部HTML则可以直接插入到页面中 # 返回评论列表app.route(/comments/int:post_id)def get_comments(post_id):...return render_template(comments.html)
2、JSON数据
JSON数据可以直接在JavaScript中直接操作 app.route(/profile/int:user_id)def get_profile(user_id):...return jsonify(usernameusername,biobio)
3、空值
有时程序中的某些接收AJAX请求的视图并不需要返回数据给客户端比如用来删除文章的视图。返回空值并将状态码指定为204表示无内容 app.route(/post/delete/int:post_id,method[DELETE])def delete_post(post_id):...return , 204
4、异步加载长文章
当加载文章按钮被点击时会发送一个AJAX请求获取文章的更多内容并直接动态插入到文章下方。
5.3、HTTP服务器端推送
社交网站在导航栏实时显示新提醒和私信的数量用户的在线状态更新股价行情监控显示商品库存信息、多人游戏、文档协作等。
实现服务器端推送的一系列技术被合称为HTTP Server Push目前常用的推送技术
名称说明传统轮询在特定的时间间隔内客户端使用 AJAX 技术不断向服务器发起 HTTP 请求然后获取新的数据并更新页面长轮询和传统轮询类似但是如果服务器端没有返回数据那就保持连接一直开启 直到有数据才返回。取回数据后再次发送另一个请求Server-Sent EventsSSESSE通过HTML中的EventSource API实现。SSE 会在客户端和服务器端建立 一个单向的通道客户端监听来自服务器端的数据而服务器端可以在任意时间发送数据两者建立类似订阅发布的通信模式
在HTML5的API中还包含一个WebSocket协议它是一种基于TCP协议的全双工通信协议。实时性更强而且还可以实现双向通信。
5.4、Web安全防范
下面介绍常见几种攻击和其他常见漏洞
5.4.1、注入攻击
注入攻击包括系统命令注入、SQL注入、NoSQL注入、ORM注入等。重点介绍SQL注入
1攻击原理
在编写SQL语句时如果直接将用户传入的数据作为参数使用字符串拼接的方式插入到SQL查询中那么攻击者可以通过注入其他语句来执行攻击操作获取敏感数据、修改数据、删除数据库表...
2攻击示例
假设我们程序是一个学生信息查询程序其中某个视图函数接收用户输入的密码返回查询结果对应的数据。 app.route(/students)def body_table():password request.args.get(password)cur db.excute(SELECT * FROM students WHERE password%s; % password)results cur.fetchall()return results
如果攻击者输入的password参数值为or 11 --那么最终视图函数中被执行的SQL语句将变为 SELECT * FROM students WHERE password or 11 --; 这会吧students表中的所有记录全部查询并返回。若设为; drop table students; ---那么查询语句变为 SELECT * FROM students WHERE password; drop table students; --; 这个语句会把students表中的所有记录全部删掉。
3主要防范方法
使用ORM可以一定程度上避免SQL注入问题验证输入类型。参数化查询转义特殊字符
5.4.2、XSS攻击
XSSCross-Site Scripting跨站脚本攻击历史悠久
1攻击原理
XSS是注入攻击的一种攻击者通过将代码注入被攻击者网站中用户一旦访问网页便会执行注入的恶意脚本。XSS攻击主要分为反射型XSS攻击和存储型XSS攻击
2攻击示例
反射型XSS又称为非持久型XSS。当某个站点存在CSS漏洞时这种攻击会通过URL注入攻击脚本只有当用户访问这个URL时才会执行攻击脚本。 # 包含反射型XSS漏洞app.route(/hello)def hello():name request.args.get(name)response h1Hello,%s!/h1 % name
这里未对字符串做任何处理就插入到返回的响应主体中返回给客户端。若干某个用户输入了一段JavaScript代码作为查询参数的值 http://example.com/hello?namescriptalert(Bingo!);/script 访问便会弹出相应内容。
存储型XSS也被称为持久性XSS这种类型的XSS攻击更常见危害也更大。它和上述类似不过会把攻击代码储存到数据库中任何用户访问包含攻击代码的页面都会被殃及。比如某个网站通过表单接收用户的留言如果服务器接收数据后未经处理就存储到数据库中那么用户可以在留言中插入任意Javascript代码。比如一行重定向代码 scriptwindow.location.hrefhttp://attacker.com;/script 其他用户一旦访问留言板页面就会执行其中的JavaScript脚本。被重定向到攻击者写入的站点
3主要防范措施 HTML转义对用户输入的内容进行HTML转义转义后可以确保用户输入的内容在浏览中作为文本显示而不是作为代码解析 # 使用Jinja2提供的escape()函数对用处传入的数据进行转义from jinja2 import escapeapp.route(/hello)def hello():name request.args.get(name)response h1Hello,%s!/h1 % escape(name) 验证用户输入 XSS攻击可以在任何用户可定制内容的地方进行例如图片引用、自定义链接。在某些HTML属性中使用普通的字符也可以插入JavaScript代码。所以需要做好验证工作 # 1、转义无法避免的XSS攻击情况有下链接a href{{ url }}Website/a# 如果不对url验证用户写入代码javascript:alert(Bingo!);,最终的代码就会变为a hrefjavascript:alert(Bingo!);Website/a# 2、图片img src{{ url }}# 类似用户写入123 onerroralert(Bingo!)最终的img标签就会变为img src123 onerroralert(Bingo!)
5.4.3、CSRF攻击
CSRFCross Site Request Forgery跨站请求伪造又被称为One-Click Attack或Session Riding。
1攻击原理
攻击者利用用户在浏览器中保存的认证信息想对应的站点发送伪造请求。
2攻击示例
假设我们是一个社交网站A攻击者可以是任意类型网站B。在A网站中删除账户操作通过GET请求执行 app.route(/account/delete)def delete_account():if not current_user.authenticated:abort(401)current_user.delete()return Deleted!
用户登录后访问http://example.com/account/delete就会删除账户。那么在攻击者的网站上只需要创建一个显示图片的img标签其中的src属性加入账户的URL img srchttp://example.com/account/delete 当用户访问B网站时浏览器在解析网页时会自动向img标签的src属性中的地址发起请求。吸取教训改用POST提交删除账户的请求。尽管如此攻击者只需要在B网站中内嵌一个隐藏表单然后设置在页面加载后执行提交表单的JavaScript函数仍会被执行。
3主要防范措施 正确使用HTTP方法遵循原则 将这些按钮内嵌在使用了POST方法的form元素中。 GET方法属于安全方法不会改变字眼状态仅用于获取资源因此又称幂等方法。页面中所有可以通过链接发起的请求都属于GET请求。POST方法用于创建、修改和删除资源。 CSRF令牌校验 判断请求是否来源自己的网站。通过在客户端加入伪随机数来防御CSRF攻击这个伪随机数通常被称为CSRF令牌token。对于AJAX请求我们可以在 XMLHttpRequest请求首部添加一个自定义字段X-CSRFToken来保存CSRF令牌。 通常使用扩展来实现CSRF令牌的创建和验证工作比如Flask-SeaSurf、Flask-WTF内置的CSRFProtect等。