一个由于前端缺少 encodeURIComponent 引起的登录问题的分析和解决

笔者最近三年一直在 SAP 中国研究院担任 Angular 应用开发程序员的职位,负责的产品是 SAP 电商云 Spartacus UI 的开发。Spartacus 是 SAP 公司主导的一个开源项目,Github 项目地址:github.com/SAP/spartac....

电商云 Storefront UI 界面如下,客户如果想在上面下单,需要点击 Sign In / Register 进行用户注册或者登录。

因为电商云是一款面向企业级用户的产品,不少客户在实施的过程中,选择将 SAP Customer Data Cloud(简称 CDC) 接入电商云的用户登录认证模块。CDC 允许企业轻松实施单一登录(Single Sign-On,SSO)以提供无缝的用户体验。用户只需一次登录即可访问多个企业应用程序。

除了 SSO 之外,CDC 还支持通过社交媒体账户进行登录,如 Facebook、Google 或LinkedIn. 这简化了用户注册流程,提高了转化率。

问题描述和分析

客户反映网站登录时,很少能够一次登录成功:使用 SSO 登录时,login 按钮往往要重复点好几次才能够完成正常的网站登录。

我们接到这个 incident 之后,在 Chrome 开发者工具的 network 面板里观察,发现 login 按钮点击之后,发送的 HTTP Post 请求,收到了 400 错误:

这个 HTTP Post 请求的作用,是通过从 SAP CDC 颁发的 UID 和 UIDSignature,发送到 SAP Commerce Cloud authorization server endpoint 去换取 access token.

我们逐一观察引起 HTTP 400 错误的请求 Payload,发现 UIDSignature 这个字段里,包含了一个特殊的 = 字符。

这个特殊的 = 被浏览器传递到后台后,就变成了一个空白字符。这导致了整个 authorization 请求不再有效,因此后台返回了 HTTP 400 错误。

解决方案

既然搞清了问题的来龙去脉,解决方案也就不难找到了。

这个 incident 的修复代码在这个 Pull Request 里:

可以看到,我们在 UIDSignature 字段值传递到后台时,添加了 encodeURIComponent 函数进行 encode 处理。

什么是 encodeURIComponent 函数?

encodeURIComponent 是 JavaScript 中的一个内置函数,用于将字符串中的特殊字符进行编码,以便能够在 URL 中传递或嵌入到 HTML 文档中。它的作用是将字符串中的非安全字符转换为安全的 URL 编码,这些非安全字符包括空格、标点符号、特殊符号等。

encodeURIComponent的语法如下:

javascript 复制代码
encodeURIComponent(uriComponent)

其中,uriComponent 是要进行编码的字符串。函数会返回一个新的字符串,其中的特殊字符都被替换成了特定的编码值,以确保 URL 的有效性和完整性。

为什么需要 encodeURIComponent?

在 HTTP 交互中,数据的传输通常涉及到 URL,包括 URL 参数、路径和查询字符串等。URL 是一种包含特殊字符的文本,而 HTTP 协议要求 URL 必须是有效的、符合规范的。如果 URL 中包含特殊字符而未经过编码,就会导致各种问题,包括:

  • URL 不符合规范,可能会导致请求失败或被服务器拒绝。
  • 数据传输的完整性可能会受到破坏,因为特殊字符可能被错误地解释或截断。
  • 安全性问题,未编码的 URL 可能会被用于攻击,例如跨站脚本(XSS)攻击。

为了避免这些问题,encodeURIComponent 函数成为了一个必不可少的工具,它确保了 URL 中的所有字符都被正确编码,以便安全地传输数据。

encodeURIComponent 的使用示例

为了更好地理解 encodeURIComponent 的必要性,我们通过一些示例来演示它在HTTP 交互中的应用。

1. URL参数传递

假设我们有一个搜索功能,用户可以输入关键字进行搜索。用户输入的关键字可能包含特殊字符,如空格、问号、和号等。为了将关键字作为 URL 参数传递给服务器,我们需要使用 encodeURIComponent 对其进行编码。

javascript 复制代码
const userInput = "Angular 2+ Tutorial?";

// 编码用户输入
const encodedKeyword = encodeURIComponent(userInput);

// 构建 URL
const searchURL = `https://example.com/search?keyword=${encodedKeyword}`;

// 发送请求
// 此时searchURL是安全且有效的URL

在这个示例中,用户输入的关键字包含空格和问号,这些字符都不适合直接放在 URL 中。通过使用 encodeURIComponent,我们将用户输入安全地编码为 URL 参数,确保了 URL 的有效性和完整性。

2. HTML 表单提交

在 HTML 表单中,用户可以输入各种字符,包括特殊字符。当用户提交表单数据时,表单数据通常会被编码并以 HTTP POST 或 GET 请求的方式发送到服务器。如果不使用encodeURIComponent 对表单数据进行编码,可能会导致数据传输问题。

html 复制代码
<form action="https://example.com/submit" method="post">
  <input type="text" name="username" value="John Doe">
  <input type="text" name="email" value="john.doe@example.com">
  <input type="submit" value="Submit">
</form>

在上面的表单中,如果用户在输入框中输入包含空格或其他特殊字符的内容,这些字符需要进行编码,以确保表单数据能够正确传输到服务器。

3. AJAX请求

在使用 JavaScript 进行 AJAX 请求时,encodeURIComponent 也非常有用。当使用 fetchXMLHttpRequest 发送数据时,特别是发送 POST 请求时,需要确保请求体中的数据是经过编码的。

javascript 复制代码
  username: "John Doe",
  email: "john.doe@example.com"
};

// 将 JavaScript 对象转换为 URL 编码的字符串
const encodedData = Object.keys(data)
  .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
  .join("&");

fetch("https://example.com/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: encodedData
});

在这个示例中,我们将 JavaScript 对象编码为 URL 编码的字符串,并将其作为 POST 请求的请求体发送到服务器。这确保了数据的完整性和有效性。

encodeURIComponent 的编码规则

了解 encodeURIComponent 的编码规则对于正确使用它非常重要。该函数遵循一些基本的规则来确保编码后的字符串是有效的 URL 组件。

以下是 encodeURIComponent 的编码规则:

  1. 字母和数字以及-_.~字符不会被编码。它们保持不变。

  2. 其他字符都会被编码成 % 后跟两位十六进制数字。例如,空格会被编码为%20,问号会被编码为 %3F

  3. encodeURIComponent 不会编码常见的 URL 保留字符,如:/?&等。这些字符在 URL 中具有特殊的含义,不应该被编码。

  4. 如果 encodeURIComponent 函数的参数不是字符串类型,它会将参数转换为字符串,然后再进行编码。

现在,让我们通过一些示例来演示 encodeURIComponent 的编码规则:

javascript 复制代码
const originalString = "Hello, World!";
const encodedString = encodeURIComponent(originalString

);

console.log(encodedString); // 输出 "Hello%2C%20World%21"

在这个示例中,逗号和空格被正确编码为 %2C%20,而叹号保持不变。

encodeURIComponentencodeURI 的区别

除了 encodeURIComponent 之外,JavaScript 还提供了另一个编码函数encodeURI。这两者有一些区别,因此在使用它们时需要注意。

  • encodeURI 用于编码整个 URL,而不是 URL 的一部分。它不会对常见的 URL 保留字符进行编码,包括:/?&等。这意味着我们可以使用 encodeURI 来编码整个 URL,而不用担心破坏 URL 结构。

  • encodeURIComponent 用于编码 URL 中的组件,例如查询字符串参数的值。它会对所有非安全字符进行编码,包括常见的 URL 保留字符。这使得它非常适合用于编码查询字符串参数,以确保数据的完整性。

我们通过示例来看看这两个函数之间的区别:

javascript 复制代码
const url = "https://example.com/search?q=JavaScript & Angular";
const encodedURL = encodeURI(url);
const encodedComponent = encodeURIComponent(url);

console.log(encodedURL);
// 输出 "https://example.com/search?q=JavaScript%20&%20Angular"
console.log(encodedComponent);
// 输出 "https%3A%2F%2Fexample.com%2Fsearch%3Fq%3DJavaScript%20%26%20Angular"

在这个示例中,encodeURI 只对 URL 中的空格进行了编码,而encodeURIComponent 对整个 URL 进行了编码,包括保留字符。

总结

encodeURIComponent 是 HTTP 交互中不可或缺的工具之一,它用于将字符串中的特殊字符编码为符合 URL 规范的安全字符串。通过确保数据的有效性和安全性,encodeURIComponent 有助于避免各种潜在的问题,包括 URL 的无效性、数据传输的完整性和安全性漏洞。

在构建 URL、处理用户输入、发送 JSON 数据等 HTTP 交互场景中,正确使用encodeURIComponent 是一种良好的编程实践,有助于构建安全可靠的 Web 应用程序。在编写 Web 应用程序时,请始终记住使用 encodeURIComponent 来处理包含特殊字符的数据,以确保数据的安全性和有效性。

相关推荐
小鼠米奇22 分钟前
详解Ajax与axios的区别
前端·javascript·ajax
Bunury30 分钟前
Vue3新组件transition(动画过渡)
前端·javascript·vue.js
zero.cyx35 分钟前
JS函数部分
开发语言·前端·javascript
超级小的大杯柠檬水37 分钟前
SpringBoot lombok(注解@Getter @Setter)
java·前端·spring
AvatarGiser1 小时前
《ElementUI/Plus 踩坑》el-table + sortablejs 拖拽顺序错乱(Vue2/3适用)
前端·vue.js·elementui
蓝染-惣右介1 小时前
【若依RuoYi-Vue | 项目实战】帝可得后台管理系统(二)
java·前端·后端·vue·springboot
哈哈哈哈cwl1 小时前
秒懂Vue.jsDiff算法与虚拟DOM
前端·javascript·vue.js
我码玄黄2 小时前
HTML翻牌器:用CSS和HTML元素创造动态数字展示
前端·css·html
-草莓星球杯2 小时前
若依VUE项目安全kind-of postcss vite漏洞扫描和修复
前端·javascript·vue.js
LJ小番茄2 小时前
关于wordPress中的用户登录注册等问题
前端·javascript·css·html·wordpress