从外部应用登录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
相关产品推荐
相关产品推荐

