本文目录导读:

在ThinkPHP框架中实现CSRF令牌验证主要涉及以下几个方面:
基础配置
在config目录下的配置文件中启用CSRF:
// config/middleware.php 或 config/app.php
return [
// 是否开启CSRF验证
'csrf_verify' => true,
// CSRF令牌字段名
'token_name' => '__token__',
// CSRF令牌存储方式
'token_storage' => 'session', // session 或 cookie
];
中间件实现
创建CSRF验证中间件:
// app/middleware/CSRF.php
namespace app\middleware;
use think\exception\HttpException;
use think\Response;
use think\facade\Session;
use think\facade\Cookie;
class CSRF
{
public function handle($request, \Closure $next)
{
// 验证CSRF令牌
if (!$this->checkToken($request)) {
if ($request->isAjax()) {
return json(['code' => 401, 'msg' => 'CSRF验证失败']);
}
throw new HttpException(403, 'CSRF验证失败');
}
return $next($request);
}
protected function checkToken($request)
{
// 排除GET和HEAD请求
if ($request->isGet() || $request->isHead()) {
return true;
}
// 验证令牌
$token = $request->post('__token__') ?: $request->header('X-CSRF-Token');
$sessionToken = Session::get('csrf_token');
return $token === $sessionToken;
}
protected function generateToken()
{
return md5(uniqid(mt_rand(), true));
}
}
路由和控制器使用
在路由中启用CSRF:
// route/app.php
use think\facade\Route;
// 在路由组中应用中间件
Route::group('user', function () {
Route::post('save', 'User/save');
Route::post('delete', 'User/delete');
})->middleware(\app\middleware\CSRF::class);
// 或单独路由
Route::post('user/save', 'User/save')->middleware(\app\middleware\CSRF::class);
在控制器中使用:
// app/controller/User.php
namespace app\controller;
use think\Request;
use think\facade\Session;
class User
{
public function save(Request $request)
{
// 手动验证CSRF(可选,如果使用了中间件则不需要)
if (!$this->checkToken($request)) {
return json(['code' => 401, 'msg' => 'CSRF验证失败']);
}
// 处理业务逻辑
return json(['code' => 200, 'msg' => '保存成功']);
}
protected function checkToken(Request $request)
{
$token = $request->param('__token__');
$sessionToken = Session::get('csrf_token');
return $token === $sessionToken;
}
public function getToken()
{
$token = md5(uniqid(mt_rand(), true));
Session::set('csrf_token', $token);
return $token;
}
}
模板中的使用方法
在HTML模板中添加CSRF令牌:
<!-- 表单中 -->
<form method="POST" action="/user/save">
<input type="hidden" name="__token__" value="{$Think.SESSION.csrf_token}" />
<!-- 或者使用封装的函数 -->
{csrf_field()}
<input type="text" name="name" />
<button type="submit">提交</button>
</form>
<!-- AJAX请求中 -->
<script>
// 获取CSRF令牌
let csrfToken = '{$Think.SESSION.csrf_token}';
// 使用fetch
fetch('/api/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({data: 'test'})
});
// 使用jQuery
$.ajax({
url: '/api/update',
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken
},
data: {data: 'test'}
});
</script>
完整的控制器示例
// app/controller/CSRFToken.php
namespace app\controller;
use think\facade\Session;
use think\response\Json;
class CSRFToken
{
public function getToken(): Json
{
$token = $this->generateToken();
Session::set('csrf_token', $token);
return json([
'code' => 200,
'token' => $token
]);
}
public function refreshToken(): Json
{
$token = $this->generateToken();
Session::set('csrf_token', $token);
return json([
'code' => 200,
'token' => $token
]);
}
protected function generateToken(): string
{
return md5(uniqid(mt_rand(), true) . microtime());
}
public function validate(Request $request): Json
{
$token = $request->param('__token__');
$sessionToken = Session::get('csrf_token');
if ($token === $sessionToken) {
return json(['code' => 200, 'status' => 'valid']);
}
return json(['code' => 401, 'status' => 'invalid']);
}
}
全局CSRF验证(使用全局中间件)
// app/middleware.php
return [
\app\middleware\CSRF::class
];
异常处理
// app/ExceptionHandle.php
namespace app;
use think\db\exception\DataNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\response\Json;
class ExceptionHandle extends Handle
{
public function render($request, \Throwable $e)
{
// 处理CSRF异常
if ($e instanceof HttpException && $e->getStatusCode() === 403) {
if ($request->isAjax()) {
return json(['code' => 403, 'msg' => 'CSRF验证失败,请刷新页面重试']);
}
return response('CSRF验证失败', 403);
}
return parent::render($request, $e);
}
}
注意事项
- GET请求 - 通常情况下GET请求不需要验证(但某些敏感操作除外)
- Token有效期 - 可以设置令牌有效期,过期后需要重新获取
- 多表单场景 - 每个表单都需要包含CSRF令牌
- AJAX请求 - 需要在请求头中添加CSRF令牌
- 登录状态变化 - 用户登录状态变化时应重新生成CSRF令牌
使用CSRF令牌验证能有效防止跨站请求伪造攻击,保护用户数据安全。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播