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

Vue3中用Service Worker拦截图片请求加Authorization头遇401问题求助

问题:Laravel私有图片请求无法通过Service Worker添加Authorization头

问题背景

我需要展示Laravel API提供的私有图片,所有请求必须携带包含Bearer token的Authorization头。目前用Quasar(基于Vue3)开发,不想把token直接拼在URL里,尝试用Service Worker拦截请求并添加头部,但始终返回401错误,请求头里并没有带上Authorization。

现有代码

Vue组件代码

<template>
   <img :src="file" />
</template>

<script>
export default {
  name: 'MyComponent',

  setup(props, context) {
    const file = 'http://localhost:8888/files/stream/watermark.png'

    onMounted(async () => {
      await navigator.serviceWorker
        .register('/service-worker.js', { scope: 'http://localhost:9999/files/stream/' })
        .then(registration => {
            console.log('ServiceWorker registration successful with scope: ', registration.scope)
        })
        .catch(err => {
            console.log('ServiceWorker registration failed: ', err);
        })
    })

    return {
       file
    }
  }
</script>

Service Worker代码

self.addEventListener('fetch', event => {
    event.respondWith(customHeaderRequestFetch(event))
})

function customHeaderRequestFetch(event) {
    const newRequest = new Request(event.request, {
      mode: 'cors',
      headers: {
        'Authorization': 'Bearer PyIlosdka73Jksdjklasd...'
      }
    })
    return fetch(newRequest)
}

Docker + Nginx配置

server {
    listen 80;
    server_name localhost;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    client_max_body_size 32M;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass api:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
    location /storage {
        add_header "Access-Control-Allow-Origin" "*";
    }
}

问题现象

  • Service Worker注册成功,控制台输出:ServiceWorker registration successful with scope: http://localhost:9999/files/stream/
  • 图片请求返回401,请求头中无Authorization字段,完整请求头如下:
Request Headers
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en,es;q=0.9,en-US;q=0.8,es-419;q=0.7
Connection: keep-alive
DNT: 1
Host: localhost:8888
Referer: http://localhost:9999/
sec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36

解决方案与替代方案

1. 修正Service Worker作用域问题

当前Service Worker注册的作用域是http://localhost:9999/files/stream/,但图片请求的是http://localhost:8888下的资源,跨域情况下Service Worker无法拦截不同origin的请求。

  • 解决方式:将Service Worker部署到Laravel API的域名(localhost:8888)下,或者调整作用域覆盖API的图片路径;如果必须在前端域名部署,需要确保CORS配置完全兼容跨域拦截逻辑。

2. 处理no-cors请求模式限制

从请求头可见Sec-Fetch-Mode: no-cors,这种模式下浏览器会禁止自定义请求头的传递,即使Service Worker修改了请求,Authorization头也不会被发送。

  • 替代实现:改用fetch主动请求图片(携带Authorization头),将响应转为Blob后生成Object URL给<img>标签:
<template>
   <img :src="imageUrl" />
</template>

<script>
import { ref, onMounted } from 'vue'

export default {
  name: 'MyComponent',

  setup() {
    const imageUrl = ref('')
    const fileUrl = 'http://localhost:8888/files/stream/watermark.png'
    const token = 'PyIlosdka73Jksdjklasd...'

    onMounted(async () => {
      try {
        const response = await fetch(fileUrl, {
          headers: {
            'Authorization': `Bearer ${token}`
          }
        })
        if (!response.ok) throw new Error('图片请求失败')
        const blob = await response.blob()
        imageUrl.value = URL.createObjectURL(blob)
      } catch (err) {
        console.error('加载图片失败:', err)
      }
    })

    return {
       imageUrl
    }
  }
</script>

3. 完善CORS配置

Laravel和Nginx需要允许Authorization头跨域:

  • 在Nginx的/files/stream或全局location中添加:
add_header "Access-Control-Allow-Origin" "http://localhost:9999";
add_header "Access-Control-Allow-Methods" "GET, OPTIONS";
add_header "Access-Control-Allow-Headers" "Authorization";
  • 同时在Laravel中配置CORS(例如使用barryvdh/laravel-cors包),允许前端域名和Authorization头。

4. 替代方案:使用Laravel签名URL

如果不想用fetch转Blob的方式,可以让Laravel生成带有效期的签名URL,前端直接使用该URL请求图片,无需携带Authorization头:

use Illuminate\Support\Facades\Storage;

// 生成5分钟内有效的签名URL
$url = Storage::temporaryUrl(
    'files/stream/watermark.png', now()->addMinutes(5),
    ['responseContentDisposition' => 'inline']
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 04:18:14