diff --git a/pom.xml b/pom.xml index d3a5dfe1..205ab18e 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 1.2.83 1.2.27 2.19.0 - 1.21 + 7.32.0 2.3 2.3.3 3.0.0 @@ -158,9 +158,9 @@ - eu.bitwalker - UserAgentUtils - ${bitwalker.version} + nl.basjes.parse.useragent + yauaa + ${yauaa.version} diff --git a/src/main/java/com/ruoyi/common/utils/http/UserAgentUtils.java b/src/main/java/com/ruoyi/common/utils/http/UserAgentUtils.java new file mode 100644 index 00000000..a133be20 --- /dev/null +++ b/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/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java index 73c3ed91..60b34bcd 100644 --- a/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java +++ b/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -8,6 +8,7 @@ import com.ruoyi.common.utils.AddressUtils; 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.security.ShiroUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.project.monitor.logininfor.domain.Logininfor; @@ -17,7 +18,6 @@ import com.ruoyi.project.monitor.online.domain.UserOnline; import com.ruoyi.project.monitor.online.service.IUserOnlineService; import com.ruoyi.project.monitor.operlog.domain.OperLog; import com.ruoyi.project.monitor.operlog.service.IOperLogService; -import eu.bitwalker.useragentutils.UserAgent; /** * 异步工厂(产生任务用) @@ -92,7 +92,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 = ShiroUtils.getIp(); return new TimerTask() { @@ -109,9 +109,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); // 封装对象 Logininfor logininfor = new Logininfor(); logininfor.setLoginName(username); diff --git a/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java b/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java index f2c9af93..ffd0ccfc 100644 --- a/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java +++ b/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java @@ -8,9 +8,9 @@ import org.apache.shiro.web.session.mgt.WebSessionContext; import org.springframework.stereotype.Component; import com.ruoyi.common.utils.IpUtils; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.UserAgentUtils; import com.ruoyi.project.monitor.online.domain.OnlineSession; import com.ruoyi.project.monitor.online.domain.UserOnline; -import eu.bitwalker.useragentutils.UserAgent; /** * 自定义sessionFactory会话 @@ -40,11 +40,11 @@ public class OnlineSessionFactory implements SessionFactory HttpServletRequest request = (HttpServletRequest) sessionContext.getServletRequest(); if (request != null) { - UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent")); + String userAgent = request.getHeader("User-Agent"); // 获取客户端操作系统 - String os = userAgent.getOperatingSystem().getName(); + String os = UserAgentUtils.getOperatingSystem(userAgent); // 获取客户端浏览器 - String browser = userAgent.getBrowser().getName(); + String browser = UserAgentUtils.getBrowser(userAgent); session.setHost(IpUtils.getIpAddr(request)); session.setBrowser(browser); session.setOs(os);