使用Token限制用户提交防止http请求重复提交一次有效,但经常前端取不到Token

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
我是一个有思想的硬汉。
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
我们看下界面:&一接口请求流程&&&二postman使用&从流程图中我们可以看出,一个接口请求需要设置:请求URL,请求方法,请求头,请求参数。同样的,在postman中,我们也只需要设置这四项即可完成一次请求。&GET请求示例&&说明:GET请求的参数在URL链接上,所以,GET请求的请求头与请求参数如在接口文档中无特别声明时,可以不填。&GET响应示例&&说明:特别标注出了响应HTTP状态码与响应正文,还有请求的耗时。需特别注意的是注意区别HTTP状态码与响应正文中的状态码,只有HTTP状态码是200时,才代表这个接口请求是正确的,这个是HTTP协议定义的,而响应正文的状态码,是程序员自已定义的,可以是200,也可以定义为其它值,是为了让接口使用者去区分正常数据与异常数据,比如:&&&POST请求示例一&POST表单提交,先看下图POST表单提交示例:&&&&&上图示例中设置了请求方法,请求URL,请求参数,但没有设置请求头。有一个要明确的点是,请求头中的Content-Type与请求参数的格式之间是有关联关系的:&&当选择x-www-form-urlencoded的参数方式后,postman自动的帮我们设置了Content-Type,所以不需要我们人工干预,这就是使用一款流行工具的好处,把一些基础点都帮我们处理了。&&POST请求示例二&JSON串提交,如下图示例:&&&上图中,当我们选择了JSON(application/json)时,postman同样帮我们自动设置了Content-Type,可以自行的去查看一个Headers.&POST请求示例三&HTTP的POST请求的参数,都是放在请求正文中的,只是根据Content-Type来判断请求正文的格式,那么我们同样可以在表单提交时,选择raw,然后自行设置Content-Type为application/x-www-form-urlencoded。&&&postman断言&一个完整的接口测试,包括:请求-&获取响应正文-&断言,我们已经知道了请求与获取响应正文,接下来将会告诉大家如何用postman进行断言。&&&这个”Tests”就是我们需要处理断言的地方,postman很人性化的帮我们把断言所用的函数全给准备好了:&&&&虽然都是英文,但看懂应该并不懂。OK,英文看着烦,不想看,是吧,那好,我们来设置一个断言场景,根据这个断言场景,来教大家如何来用postman的断言,场景如下:&1. &判断HTTP返回状态码为2002. &判断响应正文中是否包含:"statusCode":2003. &解析响应正文,并判断statusCode的值是200,message的值是”Success.”&在SNIPPETS中,往下拉,有一项”Status code:Code is 200”,这个就是为场景中的第一条准备的,就是判断HTTP返回状态码是否为200。点击这一项,可以看到在其左边出现了:&&&解释一下这句代码的意思:tests["Status code is 200"]中的tests是一个内置对象,tests["Status code is 200"]是指为这个断言起个名称叫”Status code is 200”,这个名称可以自行修改。&responseCode.code === 200 中的responseCode是内置对象,responseCode对象中有个属性是code,是指HTTP状态码的code,判断code是否为200.&综合起来,这句代码的意思是:名称为”Status code is 200”的断言中,判断responseCode对象的code属性值是否为200。&同样在SNIPPETS中,找到一项”Response body:Contains string”,顾名思义,这条就是为场景中的第二条准备的,点击后,在其左边出现了:&&我们需要修改一下:&&场景中的第三条,很显然,我们需要解析JSON串了,所以,在SNIPPETS中找到”Response body:JSON value check”并点击,在其左边出现了:&&&&我们可以看出,这里面其实是JS代码,jsonData变量其实是解析完JSON后的对象,在JS中,一个JSON对象获取其属性的值,直接是用jsonData.value,于是,我们把代码给修改一下:&&&&这样一来,我们可以看到一共有tests的断言4个,点击Send,发送请求,在响应区内可以看到如下图:&&&表示我们的断言全部通过。SNIPPETS中还有很多的函数提供给我们了,大家可以自行去体验一番。师父领进门,修行靠个人!努力吧,测试君!&&postman的基本使用,已经跟大家讲了,并收到了一些反馈,希望能讲讲postman如何一次运行多个接口请求。歪果仁的技术思想不得不佩服,想用者之所想,把管理用例与运行用例集成在了一起。让我们一起去歪果仁的技术思想中浪里个浪去吧!&管理用例---CollectionsCollections,集合。也就是将多个接口请求可以放在一起,并管理起来。什么样的接口请求可以放在同一个collection里?在这里告诉大家可以这样:一个工程一个Collection,这样方便查找及统一处理数据。也可以这样理解:collection即工程。&&第一步,创建一个Collections点击上图中的带+号的图标&&&输入Name:”demo”,Description:”demo for baixiaosheng”,点击Create按钮即创建成功一个Collections.&&&&第二步,往Collections里添加请求。&&&将准备好的接口请求点击如上按钮&&&&按上图选择好Collection及填写好Request name后,点击右下角的Add to collection按钮,即将一个请求添加进了Collection。&&&Collection精细化---Folder在前面讲到了collection即工程的概念,工程是可以管理,也是可以模块化的。随着放入Collection的请求越来越多,混乱就又出现了,在找一个请求时,要找半天,于是将collection中的请求分门类别就很重要了,歪果仁也想到了,于是,在collection中就可以添加Folder了,将相同场景的请求放入同一个Folder中,于是就实现了模块化的管理了。&&&&点击上图中的Add Folder后&&&添加上Folder name,即模块名称后,点击Create,创建成功一个Folder。&接下来,只需要把相同场景的请求拖入相同的Folder即可,这样就实现了模块化的管理了。模块化以后的结构:&&&&&运行Collection将工程模块化的用例管理起来后,借着这个管理起来的东风,也可以将工程模块化的用例执行起来,即一次执行一整个collection里的用例,或者执行一个collection里的某一个Folder里的用例。&&&点击Runner,&&&上图中的”Choose collection or folder”,如果选择demo,表示运行demo这一整个collection的用例,如果选择GET,即只运行demo下的GET模块下的用例。&Environment,即运行环境,是开发环境还是测试环境,需事先配置,大家可以下去自已尝试一下。&Iterations,即重复运行次数。会将选择好的collection中folder重复运行。&Delay,间隔时间。用例与用例间的间隔时间。&Data,外部数据加载,即用例的参数化,可以与Iterations结合起来用,实现参数化,也就是数据驱动。&Start Test Run,点击运行,运行完成后,即可得出一个简易的聚合报告。&Collection运行参数化&在Iterations重复运行时,如果某个用例希望每次运行时,使用不同的数据,那么应该满足如下2个条件:&&脚本中要用到数据的地方参数化,即用一个变量来代替,每次运行时,重新获取当前的运行数据。需要有一个数据池,这个数据池里的数据条数,要与重复运行的次数相同。&Postman的runner给我们提供了Iterations的输入项,也提供了Data的文件选择项,也就是意味着数据池是一个外部文件。如果Iterations里的值为2,那么,这个外部文件里也应该有两条数据,postman希望我们这个外部文件里的数据是一个json(当然也可以是其它的数据格式),那么,为了表示两条数据,这个json应该是一个list结构,同时,由于脚本要用到数据的地方需要参数化,需要变量,所以,每一条数据应该就是一个map,map的key对应脚本中的变量.&&&上图中表示提供了一个msg的变量,每次运行对应不同的值,预示着在脚本中可以用到msg这个变量,那在脚本中如何用?&&&如上图断言中用data.msg,其中data是个内置对象,即代表每一次运行的那个map数据,所以,可以用data.msg来获取每次运行的对应的值,当然,由于是个map,也可以用data[‘msg’]来获取对应的值。具体实践操作步骤:下载安装。页面访问请求:点击应用中“Postman”,会弹出登陆页面,完成登陆,进入Postman主页面在Get后面的输入框中输入:,点击send按钮请求参数修改百度搜索内容参数化:/s?wd=111111&rsv_spt=1&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=6&rsv_sug1=5&rsv_sug2=0&inputT=1212&rsv_sug4=1212。针对请求中的wd:111111,进行修改将此地址复制到Postman的地址栏中,点击Params将wd对应的值“111111”,修改为“222222”,点击Send。请求的搜索内容变为“222222”的搜索结果验证接口请求验证返回的页面中包括指定的字符串:页面中包括“222222”点击地址栏下面的Tests页卡,进入Tests脚本编写页面点击右侧的“Response body:Contains string”,脚本编辑框中会显示出验证的具体脚本:tests["Body matches string"] = responseBody.has("string_you_want_to_search");修改“string_you_want_to_search”为“222222”,完成验证代码的编写点击Send按钮,重新发送请求,并执行测试:Tests(1/2)管理请求保存请求-添加“打开百度首页请求”设置请求方式为Get,地址为.点击右侧保存按钮在弹出的保存请求中设置保存的内容。Collections:百度,Request name:打开百度首页。点击“Add to Collections”此请求会被保存到Collections页卡内后期在使用时,仅需要在此Collections中找到对应的请求名,即可直接使用请求多请求维护-模块管理Folder点击“百度”后面的"...",选择"Add Folder"在弹出框中,Folder name输入“01首页”,保存。拖动请求“打开百度首页”至“01首页”文件夹中Collections(系统:百度)-Folder(模块首页)-Request(请求:打开百度首页),三级的关系已经建立。可用此方法来做对应的扩展环境变量可直接通过切换环境来实现多个环境中的参数切换。常用功能:环境地址切换、全局变量使用环境请求地址切换在"百度"下,新增”Cnblos随笔“文件夹开发环境地址/s/p/4814756.html测试环境地址1/s/p/4814756.html选择“Manage Environments--Add”,在页面中设置环境信息:Environment(开发)-&key(URL)-&Value(),选择Submit再添加测试环境:Environment(测试)-&key(URL)-&Value(1)在“Cnblogs随笔”文件夹内,添加请求“打开个人中心页面”:/s/p/4814756.html,修改“/”为"{{URL}}"实操选择Environment中的“测试”,点击“send”发送请求。即执行测试环境的请求选择Environment中的“开发”,点击“send”发送请求。即执行开发环境的请求全局变量使用选择“Manage Environments--Globals”在页面中输入key(user):value(s),点击Submit修改“打开个人中心页面”中请求地址内"s"为"{{user}}"在测试“开发”、“测试”环境时,仅需要切换环境即可完成对此user的不同环境的测试。user可随意替换成别的user名执行测试-请求:/s/p/4814756.html,对此页面的返回状态码为200进行验证(具体操作,可查)选择Postman主页中Runner,会弹出运行测试的界面选择"Cnblogs随笔"文件夹-"开发"环境点击"Start Test Run",查看运行测试结果分享请求CollectionShare链接:点击Collection的'...'内的ShareShare文件:点击Collection的"Download",会下载一个名为"*.json.postman_collection".(此文件可直接导入到Postman中)Environment每个Environment中有对应的Download,会下载一个名为“*.postman_environment”.(此文件可导入Postman中)示例受&&启发,Postman也提供了一套入门的API&&,接下来我们将利用这套API做完整的测试。1. 创建一个环境变量&点击de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 color: rgb(199, 37, 78); padding: 2px 4 border-radius: 3 background-color: rgb(249, 242, 244);"
&Manage Environmentsde&,然后点击de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 color: rgb(199, 37, 78); padding: 2px 4 border-radius: 3 background-color: rgb(249, 242, 244);"
&Addde&添加一个URL变量,我们会在后续使用2. 请求一个新用户我们需要通过发送一个POST请求到{{url}}/blog/users/来创建一个用户,并需要附加下面的参数到请求body中:注:记得将环境变量切换到,这样我们才能获取到{{url}}变量<pre style="box-sizing: border- overflow: font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 padding: 1 margin-bottom: 1.5 line-height: 1.3; word-break: break- word-wrap: break- color: rgb(51, 51, 51); border: border-radius: 3 max-height: 35 margin-top: 0px !imp"
>de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 1 color: padding: 0 border-radius: 0 white-space: overflow-wrap: background:"
"username": "abhinav",
"password": "abc"
}de&这个接口现在好像不支持创建用户了,我们假设已经创建成功了,因为这不影响我们后续操作3. 获取用户的TokenToken用于授予终端请求和访问权限的。我们可以通过POST用户名和密码请求 {{url}}/blog/users/tokens/ 来获取用户的Token,这个Token将用于其他请求中。<pre style="box-sizing: border- overflow: font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 padding: 1 margin-bottom: 1.5 line-height: 1.3; word-break: break- word-wrap: break- color: rgb(51, 51, 51); border: border-radius: 3 max-height: 35 margin-top: 0px !imp"
>de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 1 color: padding: 0 border-radius: 0 white-space: overflow-wrap: background:"
"username": "abhinav",
"password": "abc"
}de&4. 格式化JSON我们需要从上面的请求结果中获取到用户Token和用户ID,并将这两个值保存到环境变量中,以供后续使用。将下面这段代码添加到测试编辑器中:<pre style="box-sizing: border- overflow: font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 padding: 1 margin-bottom: 1.5 line-height: 1.3; word-break: break- word-wrap: break- color: rgb(51, 51, 51); border: border-radius: 3 max-height: 35 margin-top: 0px !imp"
>de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 1 color: padding: 0 border-radius: 0 white-space: overflow-wrap: background:"
&var data = JSON.parse(responseBody);
if (data.token) {
tests["Body has token"] = true;
postman.setEnvironmentVariable("user_id", data.user_id);
postman.setEnvironmentVariable("token", data.token);
tests["Body has token"] = false;
}de&5. 创建一篇文章如果上面的测试是在主窗口或者集合运行器中执行,那么&de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 color: rgb(199, 37, 78); padding: 2px 4 border-radius: 3 background-color: rgb(249, 242, 244);"
&user_idde&&和&de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 color: rgb(199, 37, 78); padding: 2px 4 border-radius: 3 background-color: rgb(249, 242, 244);"
&tokende&&会自动地被添加到环境变量中。为了创建一篇文章,我们需要发送一个POST请求到 {{url}}/blog/posts ,并将&de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 color: rgb(199, 37, 78); padding: 2px 4 border-radius: 3 background-color: rgb(249, 242, 244);"
&user_idde&&和&de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 color: rgb(199, 37, 78); padding: 2px 4 border-radius: 3 background-color: rgb(249, 242, 244);"
&tokende&&添加在URL参数中。POST的请求Body如下:<pre style="box-sizing: border- overflow: font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 padding: 1 margin-bottom: 1.5 line-height: 1.3; word-break: break- word-wrap: break- color: rgb(51, 51, 51); border: border-radius: 3 max-height: 35 margin-top: 0px !imp"
>de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 1 color: padding: 0 border-radius: 0 white-space: overflow-wrap: background:"
"post": "This is a new post"
}de&6. 检查返回数据如果上述的请求成功的话将返回一个带有de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 color: rgb(199, 37, 78); padding: 2px 4 border-radius: 3 background-color: rgb(249, 242, 244);"
&post_idde&的JSON。我们将在这里验证是否创建文章成功,并且将文章ID保存到环境变量。将下面这段代码添加到测试编辑器中:<pre style="box-sizing: border- overflow: font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 padding: 1 margin-bottom: 1.5 line-height: 1.3; word-break: break- word-wrap: break- color: rgb(51, 51, 51); border: border-radius: 3 max-height: 35 margin-top: 0px !imp"
>de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 1 color: padding: 0 border-radius: 0 white-space: overflow-wrap: background:"
&var data = JSON.parse(responseBody);
if (data.post_id) {
tests["post_id found"] = true;
postman.setEnvironmentVariable("post_id", data.post_id);
tests["post_id found"] = false;
}de&7. 获取一篇文章并验证JSON我们将通过上面返回的文章ID来获取我们创建的文章。这里我们将用到Postman内置的&&JSON 验证器来检查服务器响应的JSON。创建一个GET请求到 {{url}}/blog/posts/{{post_id}},并将下面这段代码添加到测试编辑器中:<pre style="box-sizing: border- overflow: font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 0.93 padding: 1 margin-bottom: 1.5 line-height: 1.3; word-break: break- word-wrap: break- color: rgb(51, 51, 51); border: border-radius: 3 max-height: 35 margin-top: 0px !imp"
>de style="box-sizing: border- font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', font-size: 1 color: padding: 0 border-radius: 0 white-space: overflow-wrap: background:"
&var schema = {
"type": "object",
"properties": {
"content": "string",
"created_at": "integer",
"id": "integer"
"required": ["content", "created_at", "id"]
var data = JSON.parse(responseBody);
var result = tv4.validateResult(data, schema);
tests["Valid schema"] = result. de&8. 一键运行与分享集合我们将上述每一个测试保存到PostmanTest的集合中,这样我们就可以在任何时候打开和运行你想要的测试,并且可以一键运行所有,或者将集合分享给你的小伙伴,也可以获取嵌入式代码(如下面的按钮)。
阅读(1922)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'Postman前端HTTP请求调试神器教程',
blogAbstract:'\t前段时间偶尔发现了一个很好用的http请求调试工具,对于我们前端同学来说,应该说是必学的。因为不管是火狐还是谷歌浏览器自带的调试工具只能解决一些常规问题,功能也不够强大。废话不多说,进入我们今天的主题:Postman功能:主要用于模拟网络请求包快速创建请求回放、管理请求',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:1,
publishTime:9,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:2,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'我是一个有思想的硬汉。',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}原标题:【第712期】前后端分离架构下CSRF防御机制
  hi,周末来了,这周最火的莫过于微信小程序,周四下班回家破天荒打开电脑案例装逼了一下。小程序一出现早上占据了各大新闻,公众号头条,晚上它破解版就刷遍了朋友圈,这样还能好好上班吗?ok,今天我们来看看趣店@xiongwilee童鞋的分享。
  正文从这开始~
  1、什么是CSRF攻击?
  这里不再介绍CSRF,已经了解CSRF原理的同学可以直接跳到:“3、前后端分离下有何不同?”。
  不太了解的同学可以看这两篇对CSRF介绍比较详细的参考文章:
CSRF 攻击的应对之道
浅谈CSRF攻击方式
  如果来不及了解CSRF的原理,可以这么理解:有一个人发给你一个搞(mei)笑(nv)图片链接,你打开这个链接之后,便立刻收到了短信:你的银行里的钱已经转移到这个人的帐户了。
  2、有哪些防御方案?
  上面这个例子当然有点危言耸听,当然可以确定的是确实会有这样的漏洞:你打开了一个未知域名的链接,然后你就自动发了条广告帖子、你的Gmail的邮件内容就泄露了、你的百度登录状态就没了……
  防御方案在上面的两篇文章里也有提到,总结下,无外乎三种:
用户操作限制,比如验证码;
请求来源限制,比如限制HTTP Referer才能完成操作;
token验证机制,比如请求数据字段中添加一个token,响应请求时校验其有效性;
  第一种方案明显严重影响了用户体验,而且还有额外的开发成本;第二种方案成本最低,但是并不能保证100%安全,而且很有可能会埋坑;第三种方案,可取!
  token验证的CSRF防御机制是公认最合适的方案,也是本文讨论的重点。
  3、前后端分离下有何不同?
  《CSRF 攻击的应对之道》这篇文章里有提到:
  要把所有请求都改为 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的
  我们前端架构早已经告别了服务端语言(PHP/JAVA等)绑定路由、携带数据渲染模板引擎的方式(毕竟是2011年的文章了,我们笑而不语)。
  当然, 前端不要高兴的太早:前后端分离之后,Nodejs不具备完善的服务端SESSION、数据库等功能。
  总结一下,在“更先进”的前端架构下,与以往的架构会有一些区别:
Nodejs层不处理SESSION,无法直接实现会话状态数据保存;
所有的数据通过Ajax异步获取,可以灵活实现token方案;
  实现思路
  如上文提到,这里仅仅讨论在“更先进”的前端后端架构背景下的token防御方案的实现。
  1、可行性方案
  token防御的整体思路是:
  第一步:后端随机产生一个token,把这个token保存在SESSION状态中;同时,后端把这个token交给前端页面;
  第二步:下次前端需要发起请求(比如发帖)的时候把这个token加入到请求数据或者头信息中,一起传给后端;
  第三步:后端校验前端请求带过来的token和SESSION里的token是否一致;
  上文提到过,前后端分离状态下,Nodejs是不具备SESSION功能的。那这种token防御机制是不是就无法实现了呢?
  肯定不是。我们可以借助cookie把这个流程升级下:
  第一步:后端随机产生一个token,基于这个token通过SHA-56等散列算法生成一个密文;
  第二步:后端将这个token和生成的密文都设置为cookie,返回给前端;
  第三步:前端需要发起请求的时候,从cookie中获取token,把这个token加入到请求数据或者头信息中,一起传给后端;
  第四步:后端校验cookie中的密文,以及前端请求带过来的token,进行正向散列验证;
  当然这样实现也有需要注意的:
散列算法都是需要计算的,这里会有性能风险;
token参数必须由前端处理之后交给后端,而不能直接通过cookie;
cookie更臃肿,会不可避免地让头信息更重;
  现在方案确定了,具体该如何实现呢?
  2、具体实现
  我们的技术栈是 koa(服务端) + Vue.js(前端) 。有兴趣可以看这些资料:
趣店前端团队基于koajs的前后端分离实践
koa-grace——基于koa的标准前后端分离框架
grace-vue-webpack-boilerplate
  在服务端,实现了一个token生成的中间件,koa-grace-csrf:
  在前端代码中,对发送ajax请求的封装稍作优化:
  总结一下:
Nodejs生成一个随机数,通过随机数生成散列密文;并将随机数和密文存到cookie;
客户端JS获取cookie中的随机数,通过http头信息交给Nodejs;
Nodejs响应请求,校验cookie中的密文和头信息中的随机数是否匹配;
  这里依旧有个细节值得提一下:Nodejs的上层一般是nginx,而nginx默认会过滤头信息中不合法的字段(比如头信息字段名包含“_”的),这里在写头信息的时候需要注意。
  上文也提到,通过cookie及http头信息传递加密token会有很多弊端;有没有更优雅的实现方案呢?
  3、更优雅的架构
  首先,我们明确前后端分离的一些基本原则:
  后端(Java / PHP )职责:
服务层颗粒化接口,以便前端Nodejs层异步并发调用;
用户状态保存,实现用户权限等各种功能;
  前端(Nodejs + Java)职责:
Nodejs层完成路由托管及模板引擎渲染功能
Nodejs层不负责实现任何SESSION和数据库功能
  我们提到,前端Nodejs层不负责实现任何SESSION和数据库功能,但有没有可能把后端缓存系统做成公共服务提供给Nodejs层使用呢?想想感觉前端整条路都亮了有木有?!这里先挖一个坑,后续慢慢填。
  4、延伸
  这里再顺便提一下,新架构下的XSS防御。
  犹记得,在狼厂使用PHP的年代,经常被安全部门曝出各类XSS漏洞,然后就在smaty里添加各种escape滤镜,但是添加之后发现竟然把原始数据也给转义了。
  当然,现在更多要归功于各种MVVM单页面应用:使得前端完全不需要通过读取URL中的参数来控制VIEW。
  不过,还有一点值得一提:前后端分离框架下,路由由Nodejs控制;我自己要获取的后端参数和需要用在业务逻辑的参数,在主观上前端同学更好把握一些。
  所以, 在koa(服务端) + Vue.js(前端)架构下基本不用顾虑XSS问题(至少不会被全安组追着问XSS漏洞啥时候修复)。
  要不学PHP、看Java、玩Python做全栈好了?
文中提到的链接,大家可以通过”阅读原文“查看。
微信小程序,你上车了吗?
  关于本文
  作者:@xiongwilee
  原文:/post/content/koa-grace-csrf
  欢迎投稿到前端早读课
投稿邮箱:
本文相关推荐
前后端分离架构-csrf经常发生吗-心理防御机制-cookie和session机制-微信token验证机制-csrf(跨站请求伪造)攻击-java csrf 如何解决-xss和csrf分别指-web前端的表现与数据分离-token是什么意思-诉访分离工作机制-微信url和token

我要回帖

更多关于 python 提交post请求 的文章

 

随机推荐