请求网站时与发送的用户代理不同的 Javascript 用户代理(ajax)
Posted
技术标签:
【中文标题】请求网站时与发送的用户代理不同的 Javascript 用户代理(ajax)【英文标题】:Javascript user-agent (ajax) different to sent user-agent when requesting website 【发布时间】:2018-06-02 22:00:27 【问题描述】:我注意到我的手机(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 调用中吗?
附加信息:启用“请求桌面站点”功能后,两种情况下的用户代理都是
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36
。
【问题讨论】:
您是否考虑过提交issue 描述预期结果和实际结果? 谢谢,这对我来说应该更明显。我报告了该问题,并在收到反馈后更新/回答问题。 您能否在问题中包含指向问题的链接? @floor 我在我的一个页面上注意到它,然后使用一个空白页面和一个普通的 apache/php 服务器来复制结果。 我认为它只发生在 oneplus 设备上@987654322@forums.oneplus.net/threads/… 【参考方案1】:我分析了 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(),该代码块负责添加基于平台的操作系统相关信息,可在此处找到
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* 并通过一系列字符串操作和空白修剪功能对其进行处理。 http_request_headers.cc中的AddHeadersFromString()是useragent字符串添加到请求头的接口。
注意*:但我认为标头数据不是来自 useragent.cc,因为我无法在任何地方找到此函数的调用。但我可能在这里错了。
**我相信这是 OSInfo 的值被修改的地方。任何未被识别或格式错误的空白字符都可能产生此结果。
注意**:我无法测试上述语句并证明它,因为 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
https://cs.chromium.org/chromium/src/net/http/http_request_headers.cc?type=cs&q=AddHeadersFromString&l=155
https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java?q=createLoadDataParamsWithBaseUrl&dr=CSs
我只是将这个答案包括在内,以给出可能出现问题的原因之一。如果我有更多时间,我会看看我是否可以以某种方式运行测试并证明这一点。 最后一点,这个答案没有给出任何解决问题的解决方案。它只是给出了原因。
[更新]
一个非常便宜的技巧是查看 navigator.useragent 是否具有 oneplus 值并在请求上设置 ajax 标头并发送它。这将覆盖浏览器添加用户代理标头的机制。
XMLHttpRequest.setRequestHeader(header, value)
【讨论】:
这就是我喜欢 StackExchange 的原因!非常感谢您的详细回答和您的时间。我很高兴为这个答案提供赏金!【参考方案2】:在第一个userAgent中,浏览器在发出请求前通过修改userAgent将设备识别为移动设备;因此ONEPLUS A3003
。然而,在第二个中,由于 w3 规范(Find it here),您不能修改 userAgent;因此省略了ONEPLUS A3003
。
当您使用“请求桌面站点”功能时,浏览器无需修改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
【讨论】:
浏览器不提供用户代理吗?以上是关于请求网站时与发送的用户代理不同的 Javascript 用户代理(ajax)的主要内容,如果未能解决你的问题,请参考以下文章