使用表单令牌需先开启session,session在ThinkPHP6中默认是未开启的,因为Token的值是会存到session中的,session是一个应用到整个浏览器的应用程序
在表单中使用表单令牌,首先需要设置一个隐藏域用来存放Token值
<input type="hidden" name="__token__" value="{:token()}" />
还可以直接使用
{:token_field()}
默认的令牌Token名称是__token__
,如果需要自定义名称及令牌生成规则可以使用
//__hash__是要设置的token名称,md5是加密方式
{:token_field('__hash__', 'md5')}
如果你没有使用默认的模板引擎,则需要自己生成表单隐藏域
AJAX令牌提交:
如果是AJAX提交表单,可以将Token设置到meta中
<meta name="csrf-token" content="{:token()}">
还可以写成
//会自动生成一个和上方一样的代码段
{:token_meta()}
在提交时需要在AJAX中定义请求头,不然后端无法获取准确的Token值,做不了防止重复提交工作,请求头可这样写
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
例子:
//表单
<form role="form" style="width: 450px; margin: 0 auto;">
//令牌
{:token_meta()}
<div class="form-group">
<label for="name">角色名称</label>
<input type="text" class="form-control rote_name" id="name" name="rote_name">
</div>
<div class="form-group">
<label for="name">角色描述</label>
<textarea class="form-control rote_test" name="rote_test"></textarea>
</div>
<button type="button" class="btn btn-default">提交</button>
</form>
//JQ代码段
<script>
$('.btn').click(function () {
//获取表单中的参数
var rote_name = $('.rote_name').val();
var rote_test = $('.rote_test').val();
//ajax提交
$.ajax({
//请求地址
url:"/add",
//请求方式
type:"post",
//携带的参数
data:{rote_name,rote_test},
//请求头
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
//请求传参的格式
dataType:"json",
//接收返回值
success:function (e) {
//输出在控制台
console.log(e)
}
})
})
</script>
如果没有使用路由定义,可以在控制器里面手动进行令牌验证
//添加到有令牌过来的方法中
$check = $request->checkToken('__token__');
if(false === $check) {
throw new ValidateException('invalid token');
}
如果使用路由了,可以在控制器中自动进行令牌验证
//添加到有令牌过来的方法中
$check = $request->checkToken('__token__', $request->param());
if(false === $check) {
throw new ValidateException('invalid token');
}
也有许多人问未什么要在AJAX中使用请求头来传Token,因为在checkToken中存在别人已经封装好的方法,例如:
// Header验证
if ($this->header('X-CSRF-TOKEN') && $this->session->get($token) === $this->header('X-CSRF-TOKEN')) {
// 防止重复提交
$this->session->delete($token); // 验证完成销毁session
return true;
}
这是在获取你前端页面AJAX提交中的headers中的Token值,在Token值一开始验证的时候,如果session中存在Token值且和请求头传递的值全等的话,会执行if语句中的代码,验证完成进行销毁session中的Token值,返回一个true
当你在表单连续点击两次时,第一次会进入上方的if语句中,而第二次会进入下方的if语句中
if (!$this->session->has($token)) {
// 令牌数据无效
return false;
}
第一次会正常返回你要返回的值,而第二次会抛出异常