美文网首页
Java判断请求来自win10还是win11竟然这么复杂 202

Java判断请求来自win10还是win11竟然这么复杂 202

作者: 齐格Insight | 来源:发表于2024-08-03 06:55 被阅读0次

1. 问题背景

按我们的常识,判断一个请求来自win10还是win11,一般是通过user-agent这个请求头。对于Linux系统或者Mac系统,这个方法是没有问题的,但对于win10和win11,这个方法失效了。

2. 问题来源

我们的系统里有一个记录用户请求来源的操作系统字段,如下:

image.png

我发现虽然自己是win11系统,但记录里还是Windows 10。找到代码所在地:

UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
loginUser.setBrowser(userAgent.getBrowser().getName());
loginUser.setOs(userAgent.getOperatingSystem().getName());

其中UserAgent来自这个开源包:

<dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.21</version>
</dependency>

再去看看win11系统浏览器请求的user-agent值为Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 ,这个值和win10是一样的。

3. win10和win11的user-agent

通过微软官网,发现
https://learn.microsoft.com/zh-cn/microsoft-edge/web-platform/how-to-detect-win11
Windows 11 不再支持通过 User-Agent 来区分。

User-Agent字符串不会更新为区分Windows 11和Windows 10,也不会区分 CPU 体系结构。 建议不要使用User-Agent字符串来检索用户代理数据。 不支持User-Agent客户端提示的浏览器将无法区分Windows 11和Windows 10,也无法区分 CPU 体系结构。

盲猜一下是当年win10发布的时候说,以后Windows版本会一直保持win10,但后来自己打脸了,win11出来了。索性就不通过User-Agent来区分了,因为他们值一样,区分不了。

官网提供了解决方法,可以通过 Header 里的 Sec-CH-UA-Platform-Version 这个字段来区分

image.png

并且给出了支持的浏览器


image.png

还给出了前端javascript的代码

navigator.userAgentData.getHighEntropyValues(["platformVersion"])
 .then(ua => {
   if (navigator.userAgentData.platform === "Windows") {
     const majorPlatformVersion = parseInt(ua.platformVersion.split('.')[0]);
     if (majorPlatformVersion >= 13) {
       console.log("Windows 11 or later");
      }
      else if (majorPlatformVersion > 0) {
        console.log("Windows 10");
      }
      else {
        console.log("Before Windows 10");
      }
   }
   else {
     console.log("Not running on Windows");
   }
 });

4. Java获取 Sec-CH-UA-Platform-Version 这个请求头

当通过Java获取这个请求头,我发现取不到值

String platform = ServletUtils.getRequest().getHeader("Sec-Ch-Ua-Platform");
String platformVersion = ServletUtils.getRequest().getHeader("Sec-CH-UA-Platform-Version");

还以为是我浏览器的问题,使用Edge浏览器仍然不行

image.png

再看看Edge浏览器的请求值

image.png

仍然没有这个Sec-CH-UA-Platform-Version请求头,只有Sec-Ch-Ua-Platform这个请求头。
难道微软官方给的方法不可行。

5. Sec-CH-UA-Platform-Version深入挖掘

深入挖掘,Chrome浏览器给出这样提示:
https://chromestatus.com/feature/5995832180473856

image.png

意思未来要废弃 Sec-CH-UA-*,替代使用User-Agent,感觉又回到了原点。

6. 获取Sec-CH-UA-Platform-Version的正确方法

继续深入,看这篇文章给出了获取 Sec-CH-UA-Platform-Version请求头的方法
https://51degrees.com/blog/implementing-user-agent-client-hints

6.1 Accept-CH方法

为了流量或安全考虑,浏览器默认只传这三个请求头:
Sec-CH-UA, Sec-CH-UA-Mobile, and Sec-CH-UA-Platform
这三个请求头称之为 low entropy hints.

如果想要获取更多的 UA-CH 类型请求头,则需要服务端明确,如下图所示:

image.png

所以,需要在服务端返回时加上两个请求头,如下:

response.addHeader("Accept-CH", "Sec-CH-UA, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform-Version");
response.addHeader("Vary", "Sec-CH-UA, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform-Version");

回到浏览器,我们发现加成功了:


image.png

但是,在后端仍无法获得 Sec-CH-UA-Platform-Version 值,那是因为 Accept-CH方法 这方法只能应用于同源请求里。

The browser then sends these additional UA-CH headers to any URL of the first-party domain in subsequent requests.

为什么会这样,因为我自己的应用是Vue3应用,在dev环境下它的架构是这样的

image.png

在开发环境下,API服务器与Node服务器非同源,Node服务器做了一层代理。

6.2 Delegate-CH方法

上面第一种方法解决不了,再转到第二种方法,Delegate-CH:

Delegate-CH allows for delegation of high entropy hints in a single meta tag and it tells the browser that it should include the specified Client Hints headers when it makes requests to that domain.

这个方法需要在html文件的meta标签里加下以下

<meta http-equiv="Delegate-CH" content="sec-ch-ua-full-version-list http://localhost:8090; sec-ch-ua-model http://localhost:8090; sec-ch-ua-platform http://localhost:8090; sec-ch-ua-platform-version http://localhost:8090"/>

对于Vue3应用,加在index.html文件里


image.png

这里注意填的域名为 http://localhost:8090 为前端node的地址。

这Java后端可以获取到 Sec-CH-UA-Platform-Version 这个header值,如下图所示

image.png

前端请求也带了这个值


image.png

6.3 Permissions-Policy方法

Delegate-CH方法有个问题,需要改前端代码,比较麻烦,接下来第三种方法:Permissions-Policy

A Permissions Policy is a feature that gives third-party domains selective access to browser data, enabling cross-origin User-Agent Client Hints delegation. The Permissions-Policy header will do the same job as Delegate-CH but uses HTTP response headers rather than HTML.

看描述是针对跨域请求的,服务端返回的时候,增加这个header

response.addHeader("Accept-CH","Sec-CH-UA-Platform-Version, DPR");
response.addHeader("Permissions-Policy",
         "ch-ua-platform-version=(self \"localhost:8090\"), ch-dpr=(self \"localhost:8080\")");

相关文章

网友评论

      本文标题:Java判断请求来自win10还是win11竟然这么复杂 202

      本文链接:https://www.haomeiwen.com/subject/gqjahjtx.html