diff --git a/pom.xml b/pom.xml
index f50dd49122..39247a32a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
3.1.1
2.5.15
1.2.23
- 1.21
+ 7.32.0
3.0.0
2.3.3
1.4.7
@@ -109,9 +109,9 @@
- eu.bitwalker
- UserAgentUtils
- ${bitwalker.version}
+ nl.basjes.parse.useragent
+ yauaa
+ ${yauaa.version}
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index 6e6b988080..e6d488cac1 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -77,12 +77,6 @@
poi-ooxml
-
-
- org.yaml
- snakeyaml
-
-
io.jsonwebtoken
@@ -109,8 +103,8 @@
- eu.bitwalker
- UserAgentUtils
+ nl.basjes.parse.useragent
+ yauaa
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/UserAgentUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/UserAgentUtils.java
new file mode 100644
index 0000000000..a133be2020
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/UserAgentUtils.java
@@ -0,0 +1,254 @@
+package com.ruoyi.common.utils.http;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import com.ruoyi.common.utils.StringUtils;
+import nl.basjes.parse.useragent.UserAgent;
+import nl.basjes.parse.useragent.UserAgentAnalyzer;
+
+/**
+ * UserAgent解析工具类
+ *
+ * @author ruoyi
+ */
+public class UserAgentUtils
+{
+ public static final String UNKNOWN = "";
+
+ // 浏览器正则表达式模式
+ private static final Pattern CHROME_PATTERN = Pattern.compile("Chrome/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern FIREFOX_PATTERN = Pattern.compile("Firefox/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern EDGE_PATTERN = Pattern.compile("Edg(?:e)?/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern SAFARI_PATTERN = Pattern.compile("Version/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern OPERA_PATTERN = Pattern.compile("Opera/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern IE_PATTERN = Pattern.compile("(?:MSIE |Trident/.*rv:)(\\d+)(?:\\.\\d+)*");
+ private static final Pattern SAMSUNG_PATTERN = Pattern.compile("SamsungBrowser/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern UC_PATTERN = Pattern.compile("UCBrowser/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern QQ_PATTERN = Pattern.compile("QQBrowser/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern WECHAT_PATTERN = Pattern.compile("MicroMessenger/(\\d+)(?:\\.\\d+)*");
+ private static final Pattern BAIDU_PATTERN = Pattern.compile("baidubrowser/(\\d+)(?:\\.\\d+)*");
+
+ // 操作系统正则表达式模式
+ private static final Pattern WINDOWS_PATTERN = Pattern.compile("Windows NT (\\d+\\.\\d+)");
+ private static final Pattern MACOS_PATTERN = Pattern.compile("Mac OS X (\\d+[_\\d]*)");
+ private static final Pattern ANDROID_PATTERN = Pattern.compile("Android (\\d+)(?:\\.\\d+)*");
+ private static final Pattern IOS_PATTERN = Pattern.compile("OS[\\s_](\\d+)(?:_\\d+)*");
+ private static final Pattern LINUX_PATTERN = Pattern.compile("Linux");
+ private static final Pattern CHROMEOS_PATTERN = Pattern.compile("CrOS");
+
+ private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer
+ .newBuilder().hideMatcherLoadStats()
+ .withCache(5000)
+ .showMinimalVersion()
+ .withField(UserAgent.AGENT_NAME_VERSION)
+ .withField(UserAgent.OPERATING_SYSTEM_NAME_VERSION)
+ .build();
+
+ /**
+ * 获取客户端浏览器
+ */
+ public static String getBrowser(String userAgent)
+ {
+ UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
+ String agentNameVersion = iua.get(UserAgent.AGENT_NAME_VERSION).getValue();
+ if (StringUtils.isBlank(agentNameVersion) || agentNameVersion.contains("??"))
+ {
+ return formatBrowser(userAgent);
+ }
+ return agentNameVersion;
+ }
+
+ /**
+ * 获取客户端操作系统
+ */
+ public static String getOperatingSystem(String userAgent)
+ {
+ UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent);
+ String operatingSystemNameVersion = iua.get(UserAgent.OPERATING_SYSTEM_NAME_VERSION).getValue();
+ if (StringUtils.isBlank(operatingSystemNameVersion) || operatingSystemNameVersion.contains("??"))
+ {
+ return formatOperatingSystem(userAgent);
+ }
+ return operatingSystemNameVersion;
+ }
+
+ /**
+ * 全面浏览器检测
+ */
+ private static String formatBrowser(String browser)
+ {
+ // Chrome系列浏览器
+ Matcher chromeMatcher = CHROME_PATTERN.matcher(browser);
+ if (chromeMatcher.find() && (browser.contains("Chrome") || browser.contains("CriOS")))
+ {
+ return "Chrome" + chromeMatcher.group(1);
+ }
+ // Firefox
+ Matcher firefoxMatcher = FIREFOX_PATTERN.matcher(browser);
+ if (firefoxMatcher.find())
+ {
+ return "Firefox" + firefoxMatcher.group(1);
+ }
+ // Edge浏览器
+ Matcher edgeMatcher = EDGE_PATTERN.matcher(browser);
+ if (edgeMatcher.find())
+ {
+ return "Edge" + edgeMatcher.group(1);
+ }
+ // Safari浏览器(需排除Chrome)
+ Matcher safariMatcher = SAFARI_PATTERN.matcher(browser);
+ if (safariMatcher.find() && !browser.contains("Chrome"))
+ {
+ return "Safari" + safariMatcher.group(1);
+ }
+ // 微信内置浏览器
+ Matcher wechatMatcher = WECHAT_PATTERN.matcher(browser);
+ if (wechatMatcher.find())
+ {
+ return "WeChat" + wechatMatcher.group(1);
+ }
+ // UC浏览器
+ Matcher ucMatcher = UC_PATTERN.matcher(browser);
+ if (ucMatcher.find())
+ {
+ return "UC Browser" + ucMatcher.group(1);
+ }
+ // QQ浏览器
+ Matcher qqMatcher = QQ_PATTERN.matcher(browser);
+ if (qqMatcher.find())
+ {
+ return "QQ Browser" + qqMatcher.group(1);
+ }
+ // 百度浏览器
+ Matcher baiduMatcher = BAIDU_PATTERN.matcher(browser);
+ if (baiduMatcher.find())
+ {
+ return "Baidu Browser" + baiduMatcher.group(1);
+ }
+ // Samsung浏览器
+ Matcher samsungMatcher = SAMSUNG_PATTERN.matcher(browser);
+ if (samsungMatcher.find())
+ {
+ return "Samsung Browser" + samsungMatcher.group(1);
+ }
+ // Opera浏览器
+ Matcher operaMatcher = OPERA_PATTERN.matcher(browser);
+ if (operaMatcher.find())
+ {
+ return "Opera" + operaMatcher.group(1);
+ }
+ // IE浏览器
+ Matcher ieMatcher = IE_PATTERN.matcher(browser);
+ if (ieMatcher.find())
+ {
+ return "Internet Explorer" + ieMatcher.group(1);
+ }
+ return UNKNOWN;
+ }
+
+ /**
+ * 检测操作系统
+ */
+ private static String formatOperatingSystem(String operatingSystem)
+ {
+ // Windows系统
+ Matcher windowsMatcher = WINDOWS_PATTERN.matcher(operatingSystem);
+ if (windowsMatcher.find())
+ {
+ return "Windows" + getWindowsVersionDisplay(windowsMatcher.group(1));
+ }
+ // macOS系统
+ Matcher macMatcher = MACOS_PATTERN.matcher(operatingSystem);
+ if (macMatcher.find())
+ {
+ String version = macMatcher.group(1).replace("_", ".");
+ return "macOS" + extractMajorVersion(version);
+ }
+ // Android系统
+ Matcher androidMatcher = ANDROID_PATTERN.matcher(operatingSystem);
+ if (androidMatcher.find())
+ {
+ return "Android" + extractMajorVersion(androidMatcher.group(1));
+ }
+ // iOS系统
+ Matcher iosMatcher = IOS_PATTERN.matcher(operatingSystem);
+ if (iosMatcher.find() && (operatingSystem.contains("iPhone") || operatingSystem.contains("iPad")))
+ {
+ return "iOS" + extractMajorVersion(iosMatcher.group(1));
+ }
+ // Linux系统
+ if (LINUX_PATTERN.matcher(operatingSystem).find() && !operatingSystem.contains("Android"))
+ {
+ return "Linux";
+ }
+ // Chrome OS
+ if (CHROMEOS_PATTERN.matcher(operatingSystem).find())
+ {
+ return "Chrome OS";
+ }
+ return UNKNOWN;
+ }
+
+ /**
+ * 提取优化的主版本号
+ */
+ private static String extractMajorVersion(String fullVersion)
+ {
+ if (StringUtils.isEmpty(fullVersion))
+ {
+ return StringUtils.EMPTY;
+ }
+ try
+ {
+ // 清理版本号中的非数字字符
+ String cleanVersion = fullVersion.replaceAll("[^0-9.]", "");
+ String[] parts = cleanVersion.split("\\.");
+ if (parts.length > 0)
+ {
+ String firstPart = parts[0];
+ if (firstPart.matches("\\d+"))
+ {
+ int version = Integer.parseInt(firstPart);
+
+ // 处理三位数版本号(如142 -> 14)
+ if (version >= 100)
+ {
+ return String.valueOf(version / 10);
+ }
+ return firstPart;
+ }
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ // 版本号解析失败,返回原始值
+ }
+ return fullVersion;
+ }
+
+ /**
+ * Windows版本号显示优化
+ */
+ private static String getWindowsVersionDisplay(String version)
+ {
+ switch (version)
+ {
+ case "10.0":
+ return "10";
+ case "6.3":
+ return "8.1";
+ case "6.2":
+ return "8";
+ case "6.1":
+ return "7";
+ case "6.0":
+ return "Vista";
+ case "5.1":
+ return "XP";
+ case "5.0":
+ return "2000";
+ default:
+ return extractMajorVersion(version);
+ }
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
index 98517b7819..ef88bcf8f0 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
@@ -7,6 +7,7 @@ import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.LogUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.http.UserAgentUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
@@ -14,7 +15,6 @@ import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService;
-import eu.bitwalker.useragentutils.UserAgent;
/**
* 异步工厂(产生任务用)
@@ -37,7 +37,7 @@ public class AsyncFactory
public static TimerTask recordLogininfor(final String username, final String status, final String message,
final Object... args)
{
- final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
+ final String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
final String ip = IpUtils.getIpAddr();
return new TimerTask()
{
@@ -54,9 +54,9 @@ public class AsyncFactory
// 打印信息到日志
sys_user_logger.info(s.toString(), args);
// 获取客户端操作系统
- String os = userAgent.getOperatingSystem().getName();
+ String os = UserAgentUtils.getOperatingSystem(userAgent);
// 获取客户端浏览器
- String browser = userAgent.getBrowser().getName();
+ String browser = UserAgentUtils.getBrowser(userAgent);
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
index e5179a3fad..8ba1b5afaf 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
@@ -15,10 +15,10 @@ import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.http.UserAgentUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
-import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@@ -161,12 +161,12 @@ public class TokenService
*/
public void setUserAgent(LoginUser loginUser)
{
- UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
+ String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
String ip = IpUtils.getIpAddr();
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
- loginUser.setBrowser(userAgent.getBrowser().getName());
- loginUser.setOs(userAgent.getOperatingSystem().getName());
+ loginUser.setBrowser(UserAgentUtils.getOperatingSystem(userAgent));
+ loginUser.setOs(UserAgentUtils.getBrowser(userAgent));
}
/**