URL重定向需要多次encodeURIComponent

25 阅读3分钟

前言

最近在对接公司SSO(单点登录)的登出功能时,我遇到了一个让人抓狂的问题:

需求很简单 - 用户点击登出后,需要经过三次URL跳转,最终重定向到第三方系统。
实现也很"简单"  - 我直接把所有参数拼接成一个大长URL。
结果却很"魔幻"  - 要么跳转后参数神秘消失,要么直接404,最离谱的是有时候竟然跳转到了完全错误的地址!

经过和这个问题大战三百回合后,我终于明白:URL跳转就像俄罗斯套娃,每一层都需要精心包装(编码),否则就会"散架"

分析 & 解决

下面我就用"破案式"的步骤,带大家完整复盘这个问题的:

1️⃣ 第一现场还原 - 直观感受问题现象

  1. 先跳SSO登出接口 https://sso.corp.com/logout
  2. 再跳认证中心 https://auth.corp.com/redirect
  3. 最后到监控系统 https://monitor.example.com/home
const finalUrl = `https://sso.corp.com/logout?redirect=https://auth.corp.com/redirect?target=https://monitor.example.com/home`

诡异现象

  • 有时跳到https://auth.corp.com/redirect?target=https就停了
  • 有时监控系统收到的是乱码参数
  • 最严重时直接跳到了https://example.com(危险!)

💡 关键发现:浏览器控制台看到实际请求的URL中?=都消失了!

2️⃣ 线索收集 - 对比编码前后的URL差异

未编码的URL

https://sso.corp.com/logout?redirect=https://auth.corp.com/redirect?target=https://monitor.example.com/home

正确编码后

https://sso.corp.com/logout?redirect=https%3A%2F%2Fauth.corp.com%2Fredirect%3Ftarget%3Dhttps%3A%2F%2Fmonitor.example.com%2Fhome

关键区别

字符未编码编码后
::%3A
//%2F
??%3F
==%3D

🔍 实验证明:用Postman测试发现,编码后的URL能100%正确跳转

3️⃣ 关键突破 - 为什么需要层层编码?

三层跳转的编码逻辑

deepseek_mermaid_20250714_947416.png

具体原因

  1. 防止解析歧义
    当遇到redirect=https://auth.corp.com/redirect?target=xxx时:

    • 未编码:服务器会误认为target=是第一个参数
    • 编码后:整个URL变成单个参数值
  2. 安全防御
    假设攻击者构造:

    https://sso.corp.com/logout?redirect=http://hacker.com#https://auth.corp.com
    

    编码后会变成无害字符串:

    http%3A%2F%2Fhacker.com%23https%3A%2F%2Fauth.corp.com
    

4️⃣ 完美复现 - 手把手演示正确编码方式

正确实现代码

// 第三层(最内层)
const monitorUrl = 'https://monitor.example.com/home';
const encodedMonitor = encodeURIComponent(monitorUrl); 

// 第二层
const authUrl = `https://auth.corp.com/redirect?target=${encodedMonitor}`;
const encodedAuth = encodeURIComponent(authUrl);

// 第一层(最外层)
const finalUrl = `https://sso.corp.com/logout?redirect=${encodedAuth}`;

逐层编码结果

  1. 原始第三层:
    https://monitor.example.com/home
  2. 编码后第三层:
    https%3A%2F%2Fmonitor.example.com%2Fhome
  3. 拼接第二层:
    https://auth.corp.com/redirect?target=https%3A%2F%2Fmonitor.example.com%2Fhome
  4. 编码第二层:
    https%3A%2F%2Fauth.corp.com%2Fredirect%3Ftarget%3Dhttps%253A%252F%252Fmonitor.example.com%252Fhome
    (注意:这里的%25%的编码)

5️⃣ 防范手册 - 总结编码规范和安全要点

黄金法则
✅ 所有动态生成的URL参数必须编码
✅ 嵌套多少层就编码多少次
✅ 永远使用encodeURIComponent而不是encodeURI

安全检查清单

  1. 确认每个?=都被编码
  2. 测试包含#&等特殊字符的情况
  3. 验证最终URL长度不超过浏览器限制(约2000字符)
  4. 对输入域名做白名单校验

应急锦囊
当遇到跳转异常时,可以:

  1. console.log打印每一层编码结果
  2. 使用在线URL解码工具逆向检查
  3. 在Network面板查看实际请求URL

附录:编码速查表

场景正确写法错误写法
普通参数?name=${encodeURIComponent('测试')}?name=测试
嵌套URL?redirect=${encodeURIComponent(url1)}?redirect=${url1}
多参数?x=${encodeURIComponent(a)}&y=${encodeURIComponent(b)}?x=${a}&y=${b}

再也不用担心编码问题啦! ✨