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

如何在Azure Function重定向响应中以Cookie传递Token?

跨域302重定向Set-Cookie失效的解决方案

问题分析

跨域场景下,浏览器会拦截302响应中的Set-Cookie头,这是同源策略的安全限制——除非Cookie配置了符合跨域要求的属性,或者采用其他安全的Token传递方式。

可行解决方案

方案1:返回前端跳转页面(通过POST传递Token)

放弃直接302重定向,改为返回一个包含自动提交表单的HTML页面,通过前端发起POST请求将Token安全传递到目标域名,目标域名后端接收后设置Cookie。

修改后的代码示例:

const { SAMLResponse } = qs.parse(context.req.body)
const { clientName } = context.req.params
try {
   const samlResponseFormatted = decodeURIComponent(SAMLResponse as string)
   logger.add(`SAML response formatted: ${samlResponseFormatted}`)
   const auth = await new SsoAuthenticate().getAuth(samlResponseFormatted, clientName)

   // 生成自动提交的表单页面
   const redirectHtml = `
   <!DOCTYPE html>
   <html>
   <body>
      <form id="tokenForm" method="POST" action="${auth?.redirectUrl}">
         <input type="hidden" name="token" value="${auth?.token}">
      </form>
      <script>
         document.getElementById('tokenForm').submit();
      </script>
   </body>
   </html>
   `

   context.res = {
      status: StatusCodes.OK,
      headers: { 'Content-Type': 'text/html' },
      body: redirectHtml
   }
} catch (err) {
   // 补充错误处理逻辑
}

目标域名后端需新增接收接口:

// 目标站后端示例(Express)
app.post('/auth/callback', (req, res) => {
   const { token } = req.body;
   const expirationDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
   res.cookie('token', token, {
      expires: expirationDate,
      httpOnly: true,
      secure: true, // 生产环境必须开启HTTPS
      sameSite: 'Lax'
   });
   res.redirect('/');
});

方案2:配置Cookie跨域属性(限同主域子域场景)

如果当前域名和目标域名属于同一主域下的子域(如auth.example.com和app.example.com),可通过配置Cookie的跨域属性,让302重定向时Cookie生效。

修改原代码的Set-Cookie配置:

const expirationDate = new Date(Date.now())
expirationDate.setHours(expirationDate.getHours() + 24)

const headers = {
   Location: `${auth?.redirectUrl}`,
   "Set-Cookie": `token=${auth?.token}; Expires=${expirationDate.toUTCString()}; Domain=.example.com; Path=/; Secure; SameSite=None; HttpOnly;`
}

注意事项:

  • 必须使用HTTPS协议(Secure属性强制要求)
  • SameSite=None必须配合Secure,否则浏览器会忽略该Cookie
  • Domain需设置为共同主域,确保所有子域都能访问

方案3:后端临时存储+一次性校验码(无明文Token暴露)

  1. 当前服务将Token存入临时存储(如Redis),设置短过期时间(比如5分钟),生成唯一一次性校验码作为Key
  2. 重定向到目标URL时携带校验码(路径参数或查询参数,因一次性使用,安全性可控)
  3. 目标域名后端通过校验码取出Token,设置Cookie后立即删除校验码

修改后的代码示例:

const { v4: uuidv4 } = require('uuid');
const redis = require('./redis-client'); // 已配置的Redis客户端

const { SAMLResponse } = qs.parse(context.req.body)
const { clientName } = context.req.params
try {
   const samlResponseFormatted = decodeURIComponent(SAMLResponse as string)
   logger.add(`SAML response formatted: ${samlResponseFormatted}`)
   const auth = await new SsoAuthenticate().getAuth(samlResponseFormatted, clientName)

   // 生成校验码并存入Redis,5分钟过期
   const verifyCode = uuidv4();
   await redis.setEx(verifyCode, 300, auth?.token);

   // 重定向到带校验码的目标地址
   const redirectUrl = `${auth?.redirectUrl}/auth/verify/${verifyCode}`;

   context.res = {
      status: StatusCodes.MOVED_TEMPORARILY,
      headers: { Location: redirectUrl }
   }
} catch (err) {
   // 补充错误处理逻辑
}

目标域名后端校验逻辑:

// 目标站后端示例(Express)
app.get('/auth/verify/:code', async (req, res) => {
   const { code } = req.params;
   const token = await redis.get(code);
   if (!token) {
      return res.status(400).send('无效或过期的校验码');
   }
   // 删除校验码,确保一次性使用
   await redis.del(code);

   // 设置Cookie
   const expirationDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
   res.cookie('token', token, {
      expires: expirationDate,
      httpOnly: true,
      secure: true,
      sameSite: 'Lax'
   });
   res.redirect('/');
});

方案选择建议

  • 无域名关联场景:优先选方案1或方案3
  • 同主域子域场景:优先选方案2,配置简单无需额外开发
  • 高安全性要求场景:优先选方案3,Token全程不暴露在前端

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:05:52