RuoYi 6 месяцев назад
Родитель
Сommit
eb6878e18f

+ 4 - 4
pom.xml

@@ -20,7 +20,7 @@
         <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
         <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
         <spring-boot.version>2.5.15</spring-boot.version>
         <spring-boot.version>2.5.15</spring-boot.version>
         <druid.version>1.2.23</druid.version>
         <druid.version>1.2.23</druid.version>
-        <bitwalker.version>1.21</bitwalker.version>
+        <yauaa.version>7.32.0</yauaa.version>
         <swagger.version>3.0.0</swagger.version>
         <swagger.version>3.0.0</swagger.version>
         <kaptcha.version>2.3.3</kaptcha.version>
         <kaptcha.version>2.3.3</kaptcha.version>
         <pagehelper.boot.version>1.4.7</pagehelper.boot.version>
         <pagehelper.boot.version>1.4.7</pagehelper.boot.version>
@@ -109,9 +109,9 @@
 
 
             <!-- 解析客户端操作系统、浏览器等 -->
             <!-- 解析客户端操作系统、浏览器等 -->
             <dependency>
             <dependency>
-                <groupId>eu.bitwalker</groupId>
-                <artifactId>UserAgentUtils</artifactId>
-                <version>${bitwalker.version}</version>
+                <groupId>nl.basjes.parse.useragent</groupId>
+                <artifactId>yauaa</artifactId>
+                <version>${yauaa.version}</version>
             </dependency>
             </dependency>
 
 
             <!-- pagehelper 分页插件 -->
             <!-- pagehelper 分页插件 -->

+ 2 - 8
ruoyi-common/pom.xml

@@ -77,12 +77,6 @@
             <artifactId>poi-ooxml</artifactId>
             <artifactId>poi-ooxml</artifactId>
         </dependency>
         </dependency>
 
 
-        <!-- yml解析器 -->
-        <dependency>
-            <groupId>org.yaml</groupId>
-            <artifactId>snakeyaml</artifactId>
-        </dependency>
-
         <!-- Token生成与解析-->
         <!-- Token生成与解析-->
         <dependency>
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <groupId>io.jsonwebtoken</groupId>
@@ -109,8 +103,8 @@
 
 
         <!-- 解析客户端操作系统、浏览器等 -->
         <!-- 解析客户端操作系统、浏览器等 -->
         <dependency>
         <dependency>
-            <groupId>eu.bitwalker</groupId>
-            <artifactId>UserAgentUtils</artifactId>
+            <groupId>nl.basjes.parse.useragent</groupId>
+            <artifactId>yauaa</artifactId>
         </dependency>
         </dependency>
 
 
         <!-- servlet包 -->
         <!-- servlet包 -->

+ 254 - 0
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);
+        }
+    }
+}

+ 4 - 4
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.LogUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.StringUtils;
 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.AddressUtils;
 import com.ruoyi.common.utils.ip.IpUtils;
 import com.ruoyi.common.utils.ip.IpUtils;
 import com.ruoyi.common.utils.spring.SpringUtils;
 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.domain.SysOperLog;
 import com.ruoyi.system.service.ISysLogininforService;
 import com.ruoyi.system.service.ISysLogininforService;
 import com.ruoyi.system.service.ISysOperLogService;
 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,
     public static TimerTask recordLogininfor(final String username, final String status, final String message,
             final Object... args)
             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();
         final String ip = IpUtils.getIpAddr();
         return new TimerTask()
         return new TimerTask()
         {
         {
@@ -54,9 +54,9 @@ public class AsyncFactory
                 // 打印信息到日志
                 // 打印信息到日志
                 sys_user_logger.info(s.toString(), args);
                 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();
                 SysLogininfor logininfor = new SysLogininfor();
                 logininfor.setUserName(username);
                 logininfor.setUserName(username);

+ 4 - 4
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.core.redis.RedisCache;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.StringUtils;
 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.AddressUtils;
 import com.ruoyi.common.utils.ip.IpUtils;
 import com.ruoyi.common.utils.ip.IpUtils;
 import com.ruoyi.common.utils.uuid.IdUtils;
 import com.ruoyi.common.utils.uuid.IdUtils;
-import eu.bitwalker.useragentutils.UserAgent;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.SignatureAlgorithm;
@@ -161,12 +161,12 @@ public class TokenService
      */
      */
     public void setUserAgent(LoginUser loginUser)
     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();
         String ip = IpUtils.getIpAddr();
         loginUser.setIpaddr(ip);
         loginUser.setIpaddr(ip);
         loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(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));
     }
     }
 
 
     /**
     /**