产生VerifyCsrfToken的原因主要是为了防止跨站伪造请求。给每个浏览器产生一个不同的VerifyCsrfToken,是为了保证请求的合法性。
当启用VerifyCsrfToken中间件以后,表单请求,异步请求或者socket请求,都必须带上用户唯一的VerifyCsrfToken给后台进行验证。
其中,原理如下。
一、加载VerifyCsrfToken中间件
(1)当路由中声明是用默认的web中间件以后,在web中间件中,会声明使用VerifyCsrfToken中间件。其中,关于web中间件和路由之前的关系,可以点击我写的《laravel 5.3 5.4 5.5 中 route 路由源码分析》和《laravel 5.3 5.4 5.5 中 route 路由 分割实现方式》这两篇文章查看。
(2)初始化默认中间件。
入口文件:public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
初始化系统Kernel类,然后默认初始化/app/Http/Kernel类
在/app/Http/Kernel中,指明了web中间件组使用VerifyCsrfToken中间件。所以路由中指明使用默认的web中间件,就要通过VerifyCsrfToken中间件验证。
'web' => [
....
\App\Http\Middleware\VerifyCsrfToken::class,
],
二、生成VerifyCsrfToken
核心副主函数中:vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
通过 function csrf_token()生成VerifyCsrfToken
生成步骤大致是在vendor/laravel/framework/src/Illuminate/Session/Store.php中,
通过
public function regenerateToken()
{
$this->put('_token', Str::random(40));
}
生成token
通过
public function token()
{
return $this->get('_token');
}
获取token
调用VerifyCsrfToken
直接使用csrf_token()辅助函数即可。
三、提交VerifyCsrfToken
(1)首先,laravel向外开放配置项,允许指定的路由可以不经过VerifyCsrfToken的验证。
在app/Http/Middleware/VerifyCsrfToken.php中
protected $except = [
//
];
指明所要忽略使用VerifyCsrfToken验证的路由即可。
(2)识别请求
入口中间件类:vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php
按照中间件的规则,处理其中的handle方法。
if (
$this->isReading($request) || //排除只读请求
$this->runningUnitTests() || //排除单元测试请求
$this->inExceptArray($request) || //排除在开放的配置中的不需要验证的路由
$this->tokensMatch($request) //处理token,详看步骤(3)
) {
return $this->addCookieToResponse($request, $next($request));
// token验证通过以后,给用户生成cookie
}
(3)处理token
protected function tokensMatch($request)
{
$token = $this->getTokenFromRequest($request); //获取提交过来的VerifyCsrfToken,详细看步骤(4)
return is_string($request->session()->token()) &&
is_string($token) &&
hash_equals($request->session()->token(), $token);
//检查token的合法性,判断提交过来的token跟缓存中的token是否相等。
}
(4)获取提交的VerifyCsrfToken
protected function getTokenFromRequest($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN'); //表单和请求头提交的token
if (! $token && $header = $request->header('X-XSRF-TOKEN')) { //处理ajax提交的token
$token = $this->encrypter->decrypt($header);
}
return $token;
}
四、总结流程,通过VerifyCsrfToken防止夸张。
VerifyCsrfToken作用是防止用户浏览器被挟持,通过用户浏览器,提交非法的cookie来非法获取信息。
有VerifyCsrfToken以后,VerifyCsrfToken生成在客户端,客户端提交生成的VerifyCsrfToken到后端服务器验证,验证VerifyCsrfToken跟服务器中的VerifyCsrfToken相同,则证明不存在夸张,否则存在夸张危险。