因为全站启用了csrf,因此在进行文件上传时,不论是原生的XMLHttpRequest,还是jQuery的ajax,无论是显式设置multipart/form-data,还是直接submit,都会返回403:invalid csrf token。

后来搜索发现其中几种变通方式:

1、token改用get方式传递

很显然,虽然能解决问题,但并不够优雅,也缺乏安全性。

2、改用multer,并以中间件的方式在csrf前设置

但最终的测试结果并无卵用,且并足够优雅以集成上传和数据库保存操作。

无奈,自己另辟蹊径,尝试在csrf前增加一层路由,以绕过csrf验证(暂未找到解除csrf验证的方法)。最终测试可行,但……问题来了,需要鉴权的后台页面因此需要额外执行一次鉴权,且增加了额外的路由开销,并使得代码充满了“坏味道”,并非最佳的解决之道。

因此,求助于万能的源代码,以求寻得蛛丝马迹。在csurf/index.js(并非同名文件:csrf/index.js)中,有如下一段代码:

javascriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
function defaultValue (req) { return (req.body && req.body._csrf) || (req.query && req.query._csrf) || (req.headers['csrf-token']) || (req.headers['xsrf-token']) || (req.headers['x-csrf-token']) || (req.headers['x-xsrf-token']) }
function defaultValue (req) { return (req.body && req.body._csrf) || (req.query && req.query._csrf) || (req.headers['csrf-token']) || (req.headers['xsrf-token']) || (req.headers['x-csrf-token']) || (req.headers['x-xsrf-token']) }

由此可知,默认优先从req.body中获取(即post请求体),然后依次get查询参数,以及请求头。

在文件上传例子中,因为采用FormData方式,并不能额外增加body字段,同时排除query方式,因此,很简单、很直接的方式就是增加请求头参数——知道真相的我眼泪掉下来……

问题顺利解决,解决方法只有简简单单的一行代码,并不需要如stackoverflow、segmentfault等上面的如此复杂的方案。

最后,顺带ps下:若采用jQuery的ajax方式提交FormData数据,需要将processData和contentType设为false,如此方能正常上传,否则,一个大坑在正前方等着你……