Javascript 用户代理 (ajax) 与请求网站时发送的用户代理不同
Javascript user-agent (ajax) different to sent user-agent when requesting website
我注意到我的 phone(OnePlus 3,Android 8.0.0)上的 Chrome (64.0.3282.137) 在请求网页时发送略有不同的用户代理通过 ajax.
请求
请求网页时发送此用户代理:
Mozilla/5.0 (Linux; Android 8.0.0; ONEPLUS A3003 Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
发送此用户代理进行 ajax 调用,并在调用 navigator.userAgent
时返回:
Mozilla/5.0 (Linux; Android 8.0.0; Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
区别:ONEPLUS A3003
你能告诉我为什么模型包含在本地调用中,而不是在 ajax-calls 中吗?
Additional information: With the "Request desktop site"-feature enabled the user agent is Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36
in both cases.
第一个userAgent中,浏览器在发起请求前通过修改userAgent来识别设备为移动设备;因此 ONEPLUS A3003
。然而,在第二个中,由于 w3 规范 (Find it here),您不能修改 userAgent;因此省略了 ONEPLUS A3003
.
当您使用"Request desktop site"功能时,浏览器不需要修改userAgent,因此您得到相同的userAgent。
注意:Chrome 浏览器的默认 userAgent 是:
Mozilla/5.0 (Linux; Android 8.0.0; Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
我分析了 Chromium 源代码以获得一些见解。我在 C++ 方面的新手能力只能达到一定程度。
在此代码块中检测到客户端或平台的用户代理(文件:useragent.cc)。
std::string BuildUserAgentFromProduct(const std::string& product) {
std::string os_info;
base::StringAppendF(
&os_info,
"%s%s",
getUserAgentPlatform().c_str(),
BuildOSCpuInfo().c_str());
return BuildUserAgentFromOSAndProduct(os_info, product);
}
您可以在代码块中看到 BuildOSCpuInfo(),它负责添加 os 基于平台的相关信息,可在此处找到
std::string android_build_codename = base::SysInfo::GetAndroidBuildCodename();
std::string android_device_name = base::SysInfo::HardwareModelName(); // this line in particular adds the ONEPLUS A3003
但是这个函数 (BuildUserAgentFromProduct()) 没有直接用在负责发送 http 请求的 net 模块中。
当我调查 net(http) 模块的代码时,我发现他们正在获取 useragent* 并通过一系列字符串操作和白色 space 修剪功能对其进行处理。 http_request_headers.cc 中的 AddHeadersFromString() 是将用户代理字符串添加到请求中的接口 header.
注意*:但我认为 header 数据不是来自 useragent.cc,因为我无法在任何地方找到对此函数的调用。但我在这里可能是错的。
**我相信这是修改 OSInfo 值的地方。任何无法识别或格式错误的白色 space 字符都可能导致此结果。
注意**:我无法测试并证明上面的说法,因为在 Chromium 中使用的字符串有一个名为 StringPiece 的包装器(*wrapper 只是我正在使用的一个术语,从技术上讲,它可以用我不知道的不同方式调用。)。而且我不知道如何用 C++ 为 StringPiece 编写代码。
但是下面给出了一个非常简单的例子来说明它是如何出错的。
int main()
{
std::string s = " ONEPLUS\rA3003\rBuild/OPR6.170623.013";
std::string delimiter = "\r\n"; //this is the delimeter used in chromium source code.
std::string token = s.substr(0, s.find(delimiter,0));
std::cout << token << std::endl;
return 0;
}
https://www.onlinegdb.com/SkTrbFJDz
初始用户代理字符串有值而后续 http 请求没有值的原因在于 android 中 chrome 应用程序的架构。当最初加载页面时,这些值实际上是由 chrome 应用程序设置的(一个非常大的 java 代码库,但我认为我们需要看到的核心文件是 LoadUrlParams.java)它有发送 http 请求的不同实现(这里 useragent 不是由同一个 net(http) 模块修剪,而是由 Java 实现处理),这仅在第一次加载期间发生。但是任何其他后续调用都使用浏览器的 net(http) 模块。
文件参考链接:
https://cs.chromium.org/chromium/src/content/common/user_agent.cc?sq=package:chromium&dr=CSs&l=80
我加入这个答案只是为了给出问题可能发生的原因之一。如果我有更多时间,我会看看我是否可以 运行 以某种方式进行测试并证明这一点。
最后请注意,此答案未提供解决问题的任何解决方案。它只是给出了原因。
[更新]
一个非常便宜的技巧是查看 navigator.useragent 是否具有 oneplus 值并在请求上设置 ajax headers 并发送它。这将覆盖浏览器添加用户代理的机制 header.
XMLHttpRequest.setRequestHeader(header, value)
我注意到我的 phone(OnePlus 3,Android 8.0.0)上的 Chrome (64.0.3282.137) 在请求网页时发送略有不同的用户代理通过 ajax.
请求请求网页时发送此用户代理:
Mozilla/5.0 (Linux; Android 8.0.0; ONEPLUS A3003 Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
发送此用户代理进行 ajax 调用,并在调用 navigator.userAgent
时返回:
Mozilla/5.0 (Linux; Android 8.0.0; Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
区别:ONEPLUS A3003
你能告诉我为什么模型包含在本地调用中,而不是在 ajax-calls 中吗?
Additional information: With the "Request desktop site"-feature enabled the user agent is
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36
in both cases.
第一个userAgent中,浏览器在发起请求前通过修改userAgent来识别设备为移动设备;因此 ONEPLUS A3003
。然而,在第二个中,由于 w3 规范 (Find it here),您不能修改 userAgent;因此省略了 ONEPLUS A3003
.
当您使用"Request desktop site"功能时,浏览器不需要修改userAgent,因此您得到相同的userAgent。
注意:Chrome 浏览器的默认 userAgent 是:
Mozilla/5.0 (Linux; Android 8.0.0; Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
我分析了 Chromium 源代码以获得一些见解。我在 C++ 方面的新手能力只能达到一定程度。
在此代码块中检测到客户端或平台的用户代理(文件:useragent.cc)。
std::string BuildUserAgentFromProduct(const std::string& product) {
std::string os_info;
base::StringAppendF(
&os_info,
"%s%s",
getUserAgentPlatform().c_str(),
BuildOSCpuInfo().c_str());
return BuildUserAgentFromOSAndProduct(os_info, product);
}
您可以在代码块中看到 BuildOSCpuInfo(),它负责添加 os 基于平台的相关信息,可在此处找到
std::string android_build_codename = base::SysInfo::GetAndroidBuildCodename();
std::string android_device_name = base::SysInfo::HardwareModelName(); // this line in particular adds the ONEPLUS A3003
但是这个函数 (BuildUserAgentFromProduct()) 没有直接用在负责发送 http 请求的 net 模块中。
当我调查 net(http) 模块的代码时,我发现他们正在获取 useragent* 并通过一系列字符串操作和白色 space 修剪功能对其进行处理。 http_request_headers.cc 中的 AddHeadersFromString() 是将用户代理字符串添加到请求中的接口 header.
注意*:但我认为 header 数据不是来自 useragent.cc,因为我无法在任何地方找到对此函数的调用。但我在这里可能是错的。
**我相信这是修改 OSInfo 值的地方。任何无法识别或格式错误的白色 space 字符都可能导致此结果。
注意**:我无法测试并证明上面的说法,因为在 Chromium 中使用的字符串有一个名为 StringPiece 的包装器(*wrapper 只是我正在使用的一个术语,从技术上讲,它可以用我不知道的不同方式调用。)。而且我不知道如何用 C++ 为 StringPiece 编写代码。
但是下面给出了一个非常简单的例子来说明它是如何出错的。
int main()
{
std::string s = " ONEPLUS\rA3003\rBuild/OPR6.170623.013";
std::string delimiter = "\r\n"; //this is the delimeter used in chromium source code.
std::string token = s.substr(0, s.find(delimiter,0));
std::cout << token << std::endl;
return 0;
}
https://www.onlinegdb.com/SkTrbFJDz
初始用户代理字符串有值而后续 http 请求没有值的原因在于 android 中 chrome 应用程序的架构。当最初加载页面时,这些值实际上是由 chrome 应用程序设置的(一个非常大的 java 代码库,但我认为我们需要看到的核心文件是 LoadUrlParams.java)它有发送 http 请求的不同实现(这里 useragent 不是由同一个 net(http) 模块修剪,而是由 Java 实现处理),这仅在第一次加载期间发生。但是任何其他后续调用都使用浏览器的 net(http) 模块。
文件参考链接: https://cs.chromium.org/chromium/src/content/common/user_agent.cc?sq=package:chromium&dr=CSs&l=80
我加入这个答案只是为了给出问题可能发生的原因之一。如果我有更多时间,我会看看我是否可以 运行 以某种方式进行测试并证明这一点。 最后请注意,此答案未提供解决问题的任何解决方案。它只是给出了原因。
[更新]
一个非常便宜的技巧是查看 navigator.useragent 是否具有 oneplus 值并在请求上设置 ajax headers 并发送它。这将覆盖浏览器添加用户代理的机制 header.
XMLHttpRequest.setRequestHeader(header, value)