笔者最近三年一直在 SAP 中国研究院担任 Angular 应用开发程序员的职位,负责的产品是 SAP 电商云 Spartacus UI 的开发。Spartacus 是 SAP 公司主导的一个开源项目,Github 项目地址:https://github.com/SAP/spartacus.
电商云 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
的语法如下:
encodeURIComponent(uriComponent)
其中,uriComponent
是要进行编码的字符串。函数会返回一个新的字符串,其中的特殊字符都被替换成了特定的编码值,以确保 URL 的有效性和完整性。
为什么需要 encodeURIComponent?
在 HTTP 交互中,数据的传输通常涉及到 URL,包括 URL 参数、路径和查询字符串等。URL 是一种包含特殊字符的文本,而 HTTP 协议要求 URL 必须是有效的、符合规范的。如果 URL 中包含特殊字符而未经过编码,就会导致各种问题,包括:
- URL 不符合规范,可能会导致请求失败或被服务器拒绝。
- 数据传输的完整性可能会受到破坏,因为特殊字符可能被错误地解释或截断。
- 安全性问题,未编码的 URL 可能会被用于攻击,例如跨站脚本(XSS)攻击。
为了避免这些问题,encodeURIComponent
函数成为了一个必不可少的工具,它确保了 URL 中的所有字符都被正确编码,以便安全地传输数据。
encodeURIComponent
的使用示例
为了更好地理解 encodeURIComponent
的必要性,我们通过一些示例来演示它在HTTP 交互中的应用。
1. URL参数传递
假设我们有一个搜索功能,用户可以输入关键字进行搜索。用户输入的关键字可能包含特殊字符,如空格、问号、和号等。为了将关键字作为 URL 参数传递给服务器,我们需要使用 encodeURIComponent
对其进行编码。
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
对表单数据进行编码,可能会导致数据传输问题。
在上面的表单中,如果用户在输入框中输入包含空格或其他特殊字符的内容,这些字符需要进行编码,以确保表单数据能够正确传输到服务器。
3. AJAX请求
在使用 JavaScript 进行 AJAX 请求时,encodeURIComponent
也非常有用。当使用 fetch
或 XMLHttpRequest
发送数据时,特别是发送 POST 请求时,需要确保请求体中的数据是经过编码的。
username: "John Doe",
email: "[email protected]"
};
// 将 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
的编码规则:
- 字母和数字以及
-
、_
、.
和~
字符不会被编码。它们保持不变。 - 其他字符都会被编码成
%
后跟两位十六进制数字。例如,空格会被编码为%20
,问号会被编码为%3F
。 encodeURIComponent
不会编码常见的 URL 保留字符,如:
、/
、?
、&
等。这些字符在 URL 中具有特殊的含义,不应该被编码。- 如果
encodeURIComponent
函数的参数不是字符串类型,它会将参数转换为字符串,然后再进行编码。
现在,让我们通过一些示例来演示 encodeURIComponent
的编码规则:
const originalString = "Hello, World!";
const encodedString = encodeURIComponent(originalString
);
console.log(encodedString); // 输出 "Hello%2C%20World%21"
在这个示例中,逗号和空格被正确编码为 %2C
和 %20
,而叹号保持不变。
encodeURIComponent
与encodeURI
的区别
除了 encodeURIComponent
之外,JavaScript 还提供了另一个编码函数encodeURI
。这两者有一些区别,因此在使用它们时需要注意。
encodeURI
用于编码整个 URL,而不是 URL 的一部分。它不会对常见的 URL 保留字符进行编码,包括:
、/
、?
、&
等。这意味着我们可以使用encodeURI
来编码整个 URL,而不用担心破坏 URL 结构。encodeURIComponent
用于编码 URL 中的组件,例如查询字符串参数的值。它会对所有非安全字符进行编码,包括常见的 URL 保留字符。这使得它非常适合用于编码查询字符串参数,以确保数据的完整性。
我们通过示例来看看这两个函数之间的区别:
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
来处理包含特殊字符的数据,以确保数据的安全性和有效性。