Laravel 前后端分离 csrf 防护
首先根据文档详解 csrf 的流程机制,然后分析下怎么在前后端分离的情况下进行 csrf 防护?
1. 首先介绍 token 的生成,在 Illuminate\Session\Store.php 中 start () 方法中,然后点击方法 regenerateToken () 方法中:
public function start()
{
$this->loadSession();
if (! $this->has('_token')) {
$this->regenerateToken();
}
return $this->started = true;
}
public function regenerateToken()
{
$this->put('_token', Str::random(40));
}
可见 token 是每次加载 session 时自动存入进 session 中。
2. 然后看前台获取 token 值,首先表单中 {{csrf_token ()}} 点入进去可以看到
function csrf_field()
{
return new HtmlString('<input type="hidden" name="_token" value="'.csrf_token().'">'); //点入进去csrf_token方法中
}
function csrf_token()
{
$session = app('session');
if (isset($session)) {
return $session->token();
}
throw new RuntimeException('Application session store not set.');
}
可以看到前台 token 值是从 session 存储的 token 值中获取到的。
3. 然后看中间件是怎么验证前台提交的_token 值:
在 VerifyCsrfToken.php 中继承 Middleware 类,点击进入类中查看 handle 方法
public function handle($request, Closure $next)
{
if (
$this->isReading($request) || //验证请求方式,如果不在数组['HEAD', 'GET', 'OPTIONS']中 返回false;
$this->runningUnitTests() || //查看程序是否在进行测试 正式运行返回false;
$this->inExceptArray($request) || //查看$except(白名单,不用验证_token)数组,如果请求路径在数组中返回true;
$this->tokensMatch($request) //进行token验证
) {
return $this->addCookieToResponse($request, $next($request));
}
throw new TokenMismatchException;
}
我们点击 $this->tokensMatch () 方法中可以看到请求中的 token 和 session 中的 token 是否相等:
protected function tokensMatch($request)
{
$token = $this->getTokenFromRequest($request); //从请求中获取_token值
return is_string($request->session()->token()) &&
is_string($token) &&
hash_equals($request->session()->token(), $token); //session中的token和请求中的token是否相等;
}
点击进入 $this->getTokenFromRequest () 方法中
protected function getTokenFromRequest($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
return $token;
}
从中可以看到获取请求中 token 值的几种方法:
from 表单、头部值 X-CSRF-TOKEN、头部值 X-XSRF-TOKEN 这几种方试,只要明白了中间件怎么接收 token 值进行判断,然后前台就那么几种传值方式了
第一种 form 表单传值 {{csrf_field () }} 或 ajaxpost 携带值 对应 $request->input ('_token')
第二种 header 头传值:
<meta name="csrf-token" content="{{ csrf_token() }}">
$.ajaxSetup({ //修改jquery库中代码
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
如果你用 vue,只需将 resources/assets/js/bootstrap.js 引入你的文件中 然后 meta 标签头部值放入总模板里,每次 axios 请求都会携带 X-CSRF-TOKEN 进入中间件进行验证,下面为 js 文件:
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://learnku.com/docs/laravel/csrf#csrf-x-csrf-token');
}
至此 从 token 值生成到前端携带 token 到后台验证已经完全走完一个流程了,接下来想一下假如前后端分离情况下怎么进行 token 验证
很简单每次提交前用 api 的形式获取 token 值传入前端(csrf_token () 获取 token 值),然后前端用 header 头形式或 ajax 形式传入过来就可以了。
如果感觉有用就赞一下呗!!!
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: