Webman 跨域问题,session 获取不到,请求大佬们帮助,感谢

echoshiki

问题描述

webman (本地nginx反向代理域名 webman.lc)
react (vite, locahost:5173)
webman官方user插件

已经成功配置了文档中的 跨域中间件,get/post 请求除 session/cookie 外的数据都可以接收到,但是 session/cookie 获取不到,做了最简单的测试文件,get 设置 session,post 获取session,获取不到,看 network 标头,未发送任何cookie,在响应头的 set-cookie 那边会提示:此Set-Cookie标头未指定SameSite属性,默认 SameSite=Lax

注:如果开启 vite proxy,把请求地址换成 /api/,别的完全不动,就是成功的

下面写了个简单的测试,session 为 null

程序代码或配置

// 测试接口: http://webman.lc/app/user/login/test
    public function test(Request $request) {
        if ($request->method() === 'POST') {
            return json(['code' => 1, 'method' => 'POST', 'data' => [
                'session_value' => session('test-value'),
                'info' => $request->post()
            ]]);
        } elseif ($request->method() === 'GET') {
            $value = $request->get('value'); 
            $request->session()->set('test-value', $value);
            return json(['code' => 1, 'method' => 'GET', 'data' => [
                'session' => session('test-value'),
                'value' => $value
            ]]);
        }
    }
// 前端测试
import axios from 'axios';
axios.defaults.withCredentials = true;

// 请求标头:PHPSID=923c68b641a8d9418bd18b92151f7972
const testGet = async () => {
    try {
        // 发送 GET 请求
        axios.get('http://webman.lc/app/user/login/test?value=loginTest', {
            withCredentials: true
        }).then((json) => console.log(json));
    } catch (error) {
        console.error('Error testing cookie:', error);
    }
};

const testPost = async () => {
    try {
        // 发送 POST 请求
        axios.post('http://webman.lc/app/user/login/test', {
            username: 'adelle25@example.org',
            password: '123123'
        }).then((json) => console.log(json));
    } catch (error) {
        console.error('Error testing cookie:', error);
    }
};

// testGet();
testPost();

export default () => {
    return <div>Check console for cookie info</div>;
}
// 中间件 plugin/user/app/middleware/
<?php
namespace plugin\user\app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class AccessControl implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        // 如果是options请求则返回一个空响应,否则继续向洋葱芯穿越,并得到一个响应
        $response = $request->method() == 'OPTIONS' ? response('') : $handler($request);

        // 给响应添加跨域相关的http头
        $response->withHeaders([
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Allow-Origin' => $request->header('origin', 'http://localhost:5173'),
            'Access-Control-Allow-Methods' => $request->header('access-control-request-method', 'GET, POST, OPTIONS'),
            'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', 'Content-Type, Authorization, X-Requested-With'),
        ]);

        return $response;
    }
}

// 中间件配置 plugin/user/config/
<?php

use plugin\user\app\middleware\AccessControl;

return [
    // 全局中间件
    '' => [
        // ... 这里省略其它中间件
        AccessControl::class,
    ]
];

重现问题的步骤

在前端 GET 请求设置 session 后,在 POST 请求获取 session
会发现 session 为 null

操作系统环境及workerman/webman等具体版本

操作系统:macos
webman + webman-admin 最新版

438 2 0
2个回答

latin

你的问题主要在于跨域请求时的 SameSite 属性设置以及 withCredentials 的使用。

  1. 设置 SameSite 属性
    在你的 webman 项目中,确保在设置 session 的地方添加 SameSite=NoneSecure 属性。例如:

    use Webman\Http\Response;
    use Webman\Http\Request;
    
    return new Response(200, [
       'Set-Cookie' => 'test-value=' . $value . '; Path=/; HttpOnly; SameSite=None; Secure'
    ]);
  2. 确保前后端都设置了 withCredentials
    在你的前端代码中,你已经设置了 withCredentials,确保在所有请求中都包含此设置。

    axios.defaults.withCredentials = true;
  3. 修改 Nginx 配置
    确保你的 Nginx 配置中没有阻止跨域请求,并且允许发送 credentials

    server {
       listen 80;
       server_name webman.lc;
    
       location / {
           proxy_pass http://localhost:8787;
           proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header X-Forwarded-Proto $scheme;
    
           # 允许跨域
           add_header Access-Control-Allow-Origin $http_origin;
           add_header Access-Control-Allow-Credentials true;
           add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
           add_header Access-Control-Allow-Headers 'Content-Type, Authorization, X-Requested-With';
       }
    }
  4. 确保 session 的正确设置
    在 Webman 中,确保 session 配置正确,并且在设置 session 时没有遗漏 SameSite 属性:

    // 设置 session
    $request->session()->set('test-value', $value);
    
    // 获取 session
    $session_value = $request->session()->get('test-value');
  5. 检查浏览器控制台
    打开浏览器控制台,检查网络请求的 Request HeadersResponse Headers,确保 Set-Cookie 头包含 SameSite=None; Secure 属性,并且请求头包含 Cookie

  6. 确保 HTTPS
    如果你使用的是 SameSite=None,需要确保你的站点使用 HTTPS,否则 Secure 属性将无法生效。

通过以上步骤,应该可以解决跨域请求时 sessioncookie 无法获取的问题。

替你问的AI https://www.workerman.net/ai

  • echoshiki 2024-07-25

    谢谢,和chatgpt 4.o的回答一样,但是不能解决问题,它甚至重复了nginx 与中间件里标头的设置。。。中间件、credentials 这些能查到的设置都设置了,也没报错,就是session获取不到

echoshiki

排查了两天了,就是没办法解决 T T
除了 withCredentials、跨域http头外,还能怎么排查。。。

======

更新一下,似乎这些设置都是针对跨域请求,比如 a.site.com 与 b.site.com,我把前端访问地址从 localhost:5173 修改成了 front.webman.lc:5173 , 就能顺利获取 session cookie 了

跨站的话,这些设置似乎还不够,还需要 https 的相关配置?

  • 暂无评论
×
🔝