thinkphp8 cors跨域问题

2025年1月3日更新

修改AllowCrossDomain.php以支持多个域名
<?php
declare (strict_types = 1);

namespace app\middleware;

class AllowCrossDomain
{
    /**
     * 允许跨域的域名列表
     */
    protected $allowOrigins = [
        'http://yourapi1', //api1
        'http://localhost:3000',     // 本地开发环境
        'http://localhost:5173',     // Vite 默认端口
        'http://127.0.0.1:3000',
        'http://127.0.0.1:5173'
        // 可以继续添加其他域名...
    ];

    public function handle($request, \Closure $next)
    {
        $origin = $request->header('Origin');
        
        if (in_array($origin, $this->allowOrigins)) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With');
            header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
            header('Access-Control-Allow-Credentials: true');
        }
        
        if ($request->method(true) == 'OPTIONS') {
            return response()->code(204);
        }

        return $next($request);
    }
}

Access to XMLHttpRequest at 'http://yourapi' from origin 'http://localhost:5173' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
这是一个 CORS(跨域资源共享)的问题。需要在前后端都进行相应配置。

1.后端 ThinkPHP 配置

在 ThinkPHP 中,你需要添加中间件来处理 CORS。创建一个新的中间件:AllowCrossDomain.php

<?php
declare (strict_types = 1);

namespace app\middleware;

class AllowCrossDomain
{
    public function handle($request, \Closure $next)
    {
        header('Access-Control-Allow-Origin: http://localhost:5173');
        header('Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With');
        header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Credentials: true');
        
        // 预检请求直接返回空响应
        if ($request->method(true) == 'OPTIONS') {
            return response()->code(204);
        }

        return $next($request);
    }
}

然后在配置文件middleware.php中注册这个中间件:

<?php
return [
    // 全局中间件定义
    'global' => [
        // ... 其他中间件
        \app\middleware\AllowCrossDomain::class
    ]
];

2.前端 Vue/Axios 配置

修改你的 axios 配置:axios.js

import axios from 'axios'

const axiosInstance = axios.create({
    baseURL: 'http://newsapi.xetx.top',
    timeout: 5000,
    withCredentials: true,  // 如果需要携带 cookie
    headers: {
        'Content-Type': 'application/json',
    }
})

// 请求拦截器
axiosInstance.interceptors.request.use(
    config => {
        const token = localStorage.getItem('accessToken')
        if (token) {
            config.headers['Authorization'] = `Bearer ${token}`
        }
        return config
    },
    error => {
        return Promise.reject(error)
    }
)

export default axiosInstance

3.如果使用了 Vite

vite.config.js 中添加代理配置也是一个解决方案:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      '/api': {
        target: 'http://newsapi.xetx.top',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

然后修改 axios.js 的 baseURL:

const axiosInstance = axios.create({
    baseURL: '/api',  // 改用代理地址
    // ... 其他配置
})

这些配置完成后,CORS 问题应该就能解决了。记住:

  1. Access-Control-Allow-Origin 建议设置具体的域名,而不是 *
    确保 Access-Control-Allow-Headers 包含了你需要的所有请求头
  2. 如果使用了 cookie,需要设置 Access-Control-Allow-Credentials: true
    对于预检请求(OPTIONS),需要正确处理
    如果还有问题,可以:
    检查浏览器控制台的具体错误信息
    确认后端接口是否正确响应 OPTIONS 请求
    验证前端请求头是否正确设置
无标签