Javascript user-agent(ajax)与请求网站时发送的用户代理不同

时间:2017-12-20 10:54:22

标签: javascript android ajax google-chrome user-agent

我注意到我的手机上的Chrome(64.0.3282.137)(OnePlus 3,Android 8.0.0)在请求网页时发送的用户代理略有不同,与通过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调用中吗?

  

其他信息:使用"请求桌面版网站" -feature已启用,两种情况下用户代理均为Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36

2 个答案:

答案 0 :(得分:6)

我分析了铬源代码以获得一些见解。我能用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())不是直接在net模块中使用,它负责发送http请求。

当我调查net(http)模块的代码时,我看到他们正在获得useragent *并通过一系列字符串操作和空白区域修剪功能来处理它。 http_request_headers.cc中的AddHeadersFromString()是将useragent字符串添加到请求标头的接口。

注意*:但我认为标题数据不是来自useragent.cc,因为我无法在任何地方找到此函数的调用。但我可能在这里错了。

**我相信这是OSInfo的价值被修改的地方。任何无法识别的空格字符或最初预期的错误格式都可以提供此结果。

注意**:我无法测试上述语句并证明它,因为Chromium中使用的String在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 app的体系结构。当最初加载页面时,值实际上是由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)

答案 1 :(得分:0)

在第一个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