You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Laravel CSRF令牌传入Service Worker以发起POST请求

Laravel 结合 Service Worker 发起 POST 请求时的CSRF令牌问题

问题描述

我在Laravel应用中使用Service Worker实现缓存与Web推送通知功能,需要在推送通知弹出时向用户提供「接受」「拒绝」两个操作选项,用户点击选项后向Laravel发起POST请求更新用户状态。

当前service-worker.js代码如下:

self.addEventListener('notificationclick', function(event) {
    console.log('[Service Worker] Notification click Received.');

    var notification = event.notification;
    var action = event.action;

    notification.close();

    switch (action) {
        case 'close':
            console.log('[Service Worker] close Action Clicked');
            break;
        case 'view':
            console.log('[Service Worker] view Action Clicked')
            event.waitUntil(clients.openWindow(notification.data.url));
            break;
        case 'accept-player':
            console.log('[Service Worker] accept-player Action Clicked')

           fetch(notification.data.url, {
                method: 'POST',
                headers: {
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: { accepted:1 },
            }).catch(error => {
               console.error(error)
           });

            break;
        case 'reject-player':
            console.log('[Service Worker] reject-player Action Clicked')
            fetch(notification.data.url, {
                method: 'POST',
                headers: {
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: { accepted:0 },
            }).catch(error => {
                console.error(error)
            });
            break;
    }
});

代码中针对accept-player、reject-player两个场景编写了fetch请求逻辑,但Service Worker运行环境不存在document对象,无法通过读取HTML meta标签的常规方式获取CSRF令牌,控制台抛出如下错误:

sw.js:86 Uncaught ReferenceError: document is not defined

需要可行方案解决该问题,暂不确定是否应当为对应路由关闭CSRF校验。


可行解决方案

方案1:推送下发时携带CSRF令牌(推荐,无需修改CSRF校验规则)

Service Worker独立于页面主线程运行,确实无法访问DOM对象,但完全不需要在SW运行时临时获取令牌——在服务端生成推送通知载荷时,直接把当前用户的有效CSRF令牌写入通知数据即可:

  • 修改Laravel端推送发送逻辑,在通知的data字段中附加CSRF令牌:
//  Web推送下发逻辑示例
$webPush->queueNotification(
    $subscribedUser->push_subscription,
    json_encode([
        'title' => '入队申请',
        'body' => '有玩家申请加入你的队伍',
        'data' => [
            'url' => route('player.audit', $apply->id),
            'csrf_token' => csrf_token(), // 直接生成当前用户的有效CSRF令牌
            'actions' => [
                ['action' => 'accept-player', 'title' => '接受'],
                ['action' => 'reject-player', 'title' => '拒绝']
            ]
        ]
    ])
);
  • 修改Service Worker中的fetch逻辑,直接从通知数据中读取令牌,同时修正两个原有代码问题:
    • 所有异步操作必须包裹在event.waitUntil()中,避免Service Worker在请求完成前被浏览器提前终止
    • fetch传递JSON格式body时,必须用JSON.stringify()序列化,不能直接传原生对象
// accept-player分支修改后
case 'accept-player':
    console.log('[Service Worker] accept-player Action Clicked')
    event.waitUntil(
        fetch(notification.data.url, {
            method: 'POST',
            headers: {
                'X-CSRF-TOKEN': notification.data.csrf_token,
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify({ accepted: 1 }),
        }).catch(error => {
            console.error(error)
        })
    );
    break;

// reject-player分支做相同修改,body中accepted值传0即可
case 'reject-player':
    console.log('[Service Worker] reject-player Action Clicked')
    event.waitUntil(
        fetch(notification.data.url, {
            method: 'POST',
            headers: {
                'X-CSRF-TOKEN': notification.data.csrf_token,
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify({ accepted: 0 }),
        }).catch(error => {
            console.error(error)
        })
    );
    break;

方案2:指定路由排除CSRF校验(适合简单场景)

如果不想修改推送下发逻辑,可以仅将处理通知点击回调的路由加入CSRF白名单,禁止全局关闭CSRF校验:

  • 打开app/Http/Middleware/VerifyCsrfToken.php,将对应路由加入$except数组:
protected $except = [
    // 替换为你自己的回调路由路径
    '/player/audit-callback',
];
  • 同样按照方案1中的要求修改SW的fetch逻辑:用event.waitUntil()包裹请求、序列化body即可,不需要再携带X-CSRF-TOKEN请求头。

注意:使用该方案时必须给回调接口加上严格的用户身份鉴权,比如通过请求参数关联的申请ID匹配对应用户权限,避免接口被恶意调用。


内容的提问来源于stack exchange,提问作者Mous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 02:27:31