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

Spa*_*cus 26 javascript ajax user-agent android google-chrome

我注意到我的手机上的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调用中吗?

附加信息:启用"请求桌面站点"功能后,用户代理就会 Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36出现这两种情况.

kar*_*ick 6

我分析了铬源代码以获得一些见解.我能用c ++中的新手能力达到一定程度.

在此代码块(File: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);
}
Run Code Online (Sandbox Code Playgroud)

您可以在代码块中看到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
Run Code Online (Sandbox Code Playgroud)

但是这个函数(BuildUserAgentFromProduct())不直接用在负责发送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;
}
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)