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

从外部应用登录Symfony应用遇CORS问题,求解决方案

外部应用登录Symfony应用的可行方案咨询

我需要实现从外部应用登录Symfony应用的功能,自己编写了自定义认证器,同一环境下登录流程正常,但外部应用调用时碰到CORS错误:Redirect is not allowed for a preflight request。

我尝试的流程:

  • 外部应用向Symfony应用发送GET请求获取token;
  • 携带该token和邮箱通过POST请求触发认证器,这个请求在Postman里能正常返回已登录用户的首页;
  • 不清楚后续该怎么操作才能完成正常登录,求解决思路,谢谢!

更新1:相关代码

Symfony自定义认证器代码

/**
 * @throws \Exception
 */
public function authenticate(Request $request): Passport
{
    $apiToken = $request->headers->get('X-AUTH-TOKEN-OAV');
    $sessionId = $request->headers->get('X-AUTH-TOKEN-SESSION');
    $session = $request->getSession();
    $session->setId($sessionId);

    if (null === $apiToken) {
        // The token header was empty, authentication fails with HTTP Status
        // Code 401 "Unauthorized"
        throw new CustomUserMessageAuthenticationException('X-AUTH-TOKEN-OAV attendu dans les headers');
    }

    $content = json_decode($request->getContent(), true);
    if (\is_array($content) && \array_key_exists('userEmail', $content)) {
        $email = $content['userEmail'];
        $request->getSession()->set('email', $email);
    }

    // On inscrit l'email en session
    $email = $request->getSession()->get('email');
    // On vérifie la validité du token et on incrémente la durée de sa validité
    $this->checkTokenValidity($apiToken, true);
    return new Passport(new UserBadge('mail@mail.fr'), new PasswordCredentials('pswd!!'));
}

模拟外部应用的HTML页面代码

{% block body %}
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.1.min.js"
        integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
<script>
    let tokenOav = '';
    let sessionOav = '';
    function sendSetRequest() {
        console.info('Sending set request');
        console.log(tokenOav.value);
        console.log('start');
        console.log(sessionOav)
        console.log('********')
        let set_params = {
            "action": "action_set"
        };
        return $.ajax({
            data: set_params,
            url: "https://my-site.fr/home",
            headers: {
                'X-AUTH-TOKEN-OAV': tokenOav.value,
                'withCredentials': true,
                'X-AUTH-TOKEN-SESSION': sessionOav
            },
            type: 'post',
            success: function (result) {
                window.location.href = "https://my-site.fr/home";
            }
        });
    }
    function sendGetRequest() {
        console.info('Sending get request');
        let get_params = {
            "action": "action_get"
        };
        return $.ajax({
            data: get_params,
            url: 'https://my-site.fr/api/generer-token?api_token=TOKEN',
            type: 'get',
            success: function (result) {
                console.log(result);
                tokenOav = result.token;
                sessionOav = result.session_id;
                $("#cntr_debug2").html(result);
            }
        });
    }
    function callApi() {
        sendGetRequest().then(sendSetRequest);
    }
</script>
<form><!---Removed form submit url-->
    <input type="text" id="keyName" value="testValue">
    <!---Id attribute added to input field to be submitted--->
    <input type="button" onclick="callApi()" value="Submit">
    <!---Input type is button NOT submit--->
</form>

解决思路

1. 先处理CORS预请求问题

你碰到的Redirect is not allowed for a preflight request错误,是因为浏览器发送OPTIONS预请求时,Symfony返回了重定向(比如认证成功后跳转到首页),但预请求不允许重定向。

  • 用nelmio/cors-bundle管理CORS,配置要允许外部域名的OPTIONS请求,且放行必要的请求头:
    nelmio_cors:
        defaults:
            allow_credentials: true
            allow_origin: ['https://你的外部应用域名']
            allow_headers: ['X-AUTH-TOKEN-OAV', 'X-AUTH-TOKEN-SESSION', 'Content-Type']
            allow_methods: ['GET', 'POST', 'OPTIONS']
            max_age: 3600
        paths:
            '^/api/': ~
            '^/home': ~ # 你的认证接口路径
    
  • 在自定义认证器开头跳过OPTIONS请求的认证逻辑:
    if ($request->isMethod('OPTIONS')) {
        return new Passport(new UserBadge('anon.'), new PasswordCredentials(''));
    }
    

2. 调整登录流程的前后端交互逻辑

Postman不严格遵循CORS规则所以正常,浏览器里必须调整流程:

  • 外部应用获取token后,发送POST请求到专门的认证接口(不是首页),认证成功后Symfony设置会话Cookie,返回JSON成功响应,而非直接重定向;
  • 外部应用收到成功响应后,再跳转到Symfony首页,此时浏览器会携带会话Cookie,Symfony就能识别已登录用户。

修改前端sendSetRequest逻辑:

function sendSetRequest() {
    console.info('Sending set request');
    console.log(tokenOav);
    console.log(sessionOav);
    return $.ajax({
        data: JSON.stringify({userEmail: '目标邮箱'}),
        contentType: 'application/json',
        url: "https://my-site.fr/api/authenticate", // 改为专门的认证接口
        headers: {
            'X-AUTH-TOKEN-OAV': tokenOav,
            'X-AUTH-TOKEN-SESSION': sessionOav
        },
        xhrFields: {
            withCredentials: true // 移到xhrFields里,不是headers
        },
        type: 'post',
        success: function (result) {
            if (result.success) {
                window.location.href = "https://my-site.fr/home";
            }
        }
    });
}

3. 修正会话处理逻辑

  • 不要手动设置session->setId($sessionId),跨域场景下让Symfony自动管理会话,外部应用只需通过withCredentials: true传递Cookie;
  • 配置Symfony会话Cookie的SameSite属性(跨域HTTPS场景设为None):
    framework:
        session:
            cookie_samesite: 'None'
            cookie_secure: true # 必须HTTPS才能使用None值
    

4. 测试注意事项

  • 确保外部应用和Symfony都使用HTTPS,withCredentials在HTTP环境下可能被浏览器阻止;
  • 测试前清除浏览器缓存和Cookie,避免旧会话干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:01:20