Spring Mobile是如何判断访问设备的类型的

简介: Spring最近换域名了,去转转,发现了一个有意思的项目:spring mobile。这个项目有很多实用的功能,如识别访问我们网站的设备是什么类型的(手机,平板,PC),据域名或者url来切换不同访问内容,据不同的访问设备转到不同的view中。

Spring最近换域名了,去转转,发现了一个有意思的项目:spring mobile。

这个项目有很多实用的功能,如识别访问我们网站的设备是什么类型的(手机,平板,PC),据域名或者url来切换不同访问内容,据不同的访问设备转到不同的view中。


识别访问设备类型的核心代码在org.springframework.mobile.device.LiteDeviceResolver 类中,这个类实际上根据http请求头部的User-Agent的内容来判断设备到底是哪种类型的。

可以看到,这个类实际上是很简单的。不过字符串的匹配算法可能可以优化下。

public class LiteDeviceResolver implements DeviceResolver {

	private final List mobileUserAgentPrefixes = new ArrayList();

	private final List mobileUserAgentKeywords = new ArrayList();

	private final List tabletUserAgentKeywords = new ArrayList();

	private final List normalUserAgentKeywords = new ArrayList();

	public LiteDeviceResolver() {
		init();
	}

	public LiteDeviceResolver(List normalUserAgentKeywords) {
		init();
		this.normalUserAgentKeywords.addAll(normalUserAgentKeywords);
	}

	public Device resolveDevice(HttpServletRequest request) {
		String userAgent = request.getHeader("User-Agent");
		// UserAgent keyword detection of Normal devices
		if (userAgent != null) {
			userAgent = userAgent.toLowerCase();
			for (String keyword : normalUserAgentKeywords) {
				if (userAgent.contains(keyword)) {
					return resolveFallback(request);
				}
			}
		}
		// UAProf detection
		if (request.getHeader("x-wap-profile") != null || request.getHeader("Profile") != null) {
			return LiteDevice.MOBILE_INSTANCE;
		}
		// User-Agent prefix detection
		if (userAgent != null && userAgent.length() >= 4) {
			String prefix = userAgent.substring(0, 4).toLowerCase();
			if (mobileUserAgentPrefixes.contains(prefix)) {
				return LiteDevice.MOBILE_INSTANCE;
			}
		}
		// Accept-header based detection
		String accept = request.getHeader("Accept");
		if (accept != null && accept.contains("wap")) {
			return LiteDevice.MOBILE_INSTANCE;
		}
		// UserAgent keyword detection for Mobile and Tablet devices
		if (userAgent != null) {
			userAgent = userAgent.toLowerCase();
			// Android special case 
			if (userAgent.contains("android") && !userAgent.contains("mobile")) {
				return LiteDevice.TABLET_INSTANCE;
			}
			// Kindle Fire special case 
			if (userAgent.contains("silk") && !userAgent.contains("mobile")) {
				return LiteDevice.TABLET_INSTANCE;
			}
			for (String keyword : tabletUserAgentKeywords) {
				if (userAgent.contains(keyword)) {
					return LiteDevice.TABLET_INSTANCE;
				}
			}
			for (String keyword : mobileUserAgentKeywords) {
				if (userAgent.contains(keyword)) {
					return LiteDevice.MOBILE_INSTANCE;
				}
			}
		}
		// OperaMini special case
		@SuppressWarnings("rawtypes")
		Enumeration headers = request.getHeaderNames();
		while (headers.hasMoreElements()) {
			String header = (String) headers.nextElement();
			if (header.contains("OperaMini")) {
				return LiteDevice.MOBILE_INSTANCE;
			}
		}
		return resolveFallback(request);
	}

	// subclassing hooks

	/**
	 * List of user agent prefixes that identify mobile devices.
	 * Used primarily to match by operator or handset manufacturer.
	 */
	protected List getMobileUserAgentPrefixes() {
		return mobileUserAgentPrefixes;
	}

	/**
	 * List of user agent keywords that identify mobile devices.
	 * Used primarily to match by mobile platform or operating system.
	 */
	protected List getMobileUserAgentKeywords() {
		return mobileUserAgentKeywords;
	}

	/**
	 * List of user agent keywords that identify tablet devices.
	 * Used primarily to match by tablet platform or operating system.
	 */
	protected List getTabletUserAgentKeywords() {
		return tabletUserAgentKeywords;
	}

	/**
	 * List of user agent keywords that identify normal devices.
	 * Any items in this list take precedence over the mobile and
	 * tablet user agent keywords, effectively overriding those. 
	 */
	protected List getNormalUserAgentKeywords() {
		return normalUserAgentKeywords;
	}

	/**
	 * Initialize this device resolver implementation.
	 * Registers the known set of device signature strings.
	 * Subclasses may override to register additional strings.
	 */
	protected void init() {
		getMobileUserAgentPrefixes().addAll(Arrays.asList(KNOWN_MOBILE_USER_AGENT_PREFIXES));
		getMobileUserAgentKeywords().addAll(Arrays.asList(KNOWN_MOBILE_USER_AGENT_KEYWORDS));
		getTabletUserAgentKeywords().addAll(Arrays.asList(KNOWN_TABLET_USER_AGENT_KEYWORDS));
	}

	/**
	 * Fallback called if no mobile device is matched by this resolver.
	 * The default implementation of this method returns a "normal" {@link Device} that is neither mobile or a tablet.
	 * Subclasses may override to try additional mobile or tablet device matching before falling back to a "normal" device.
	 */
	protected Device resolveFallback(HttpServletRequest request) {
		return LiteDevice.NORMAL_INSTANCE;
	}

	// internal helpers

	private static final String[] KNOWN_MOBILE_USER_AGENT_PREFIXES = new String[] { "w3c ", "w3c-", "acs-", "alav",
			"alca", "amoi", "audi", "avan", "benq", "bird", "blac", "blaz", "brew", "cell", "cldc", "cmd-", "dang",
			"doco", "eric", "hipt", "htc_", "inno", "ipaq", "ipod", "jigs", "kddi", "keji", "leno", "lg-c", "lg-d",
			"lg-g", "lge-", "lg/u", "maui", "maxo", "midp", "mits", "mmef", "mobi", "mot-", "moto", "mwbp", "nec-",
			"newt", "noki", "palm", "pana", "pant", "phil", "play", "port", "prox", "qwap", "sage", "sams", "sany",
			"sch-", "sec-", "send", "seri", "sgh-", "shar", "sie-", "siem", "smal", "smar", "sony", "sph-", "symb",
			"t-mo", "teli", "tim-", "tosh", "tsm-", "upg1", "upsi", "vk-v", "voda", "wap-", "wapa", "wapi", "wapp",
			"wapr", "webc", "winw", "winw", "xda ", "xda-" };

	private static final String[] KNOWN_MOBILE_USER_AGENT_KEYWORDS = new String[] { "blackberry", "webos", "ipod",
			"lge vx", "midp", "maemo", "mmp", "mobile", "netfront", "hiptop", "nintendo DS", "novarra", "openweb",
			"opera mobi", "opera mini", "palm", "psp", "phone", "smartphone", "symbian", "up.browser", "up.link",
			"wap", "windows ce" };

	private static final String[] KNOWN_TABLET_USER_AGENT_KEYWORDS = new String[] { "ipad", "playbook", "hp-tablet",
			"kindle" };

}



相关文章
|
Java API Spring
Spring容器如何使用一个注解来指定一个类型为配置类型
Spring容器如何使用一个注解来指定一个类型为配置类型
191 0
|
1月前
|
NoSQL Java 数据库连接
《深入理解Spring》Spring Data——数据访问的统一抽象与极致简化
Spring Data通过Repository抽象和方法名派生查询,简化数据访问层开发,告别冗余CRUD代码。支持JPA、MongoDB、Redis等多种存储,统一编程模型,提升开发效率与架构灵活性,是Java开发者必备利器。(238字)
|
2月前
|
安全 数据可视化 Java
AiPy开发的 Spring 漏洞检测神器,未授权访问无所遁形
针对Spring站点未授权访问问题,现有工具难以检测如Swagger、Actuator等组件漏洞,且缺乏修复建议。全新AI工具基于Aipy开发,具备图形界面,支持一键扫描常见Spring组件,自动识别未授权访问风险,按漏洞类型标注并提供修复方案,扫描结果可视化展示,支持导出报告,大幅提升渗透测试与漏洞定位效率。
|
11月前
|
XML 安全 Java
|
9月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
635 25
|
缓存 监控 Java
优化Spring Boot应用的数据库访问性能
优化Spring Boot应用的数据库访问性能
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
323 1
Spring高手之路24——事务类型及传播行为实战指南
|
安全 Java 数据库
后端进阶之路——浅谈Spring Security用户、角色、权限和访问规则(三)
后端进阶之路——浅谈Spring Security用户、角色、权限和访问规则(三)
|
安全 算法 Java
强大!基于Spring Boot 3.3 六种策略识别上传文件类型
【10月更文挑战第1天】在Web开发中,文件上传是一个常见的功能需求。然而,如何确保上传的文件类型符合预期,防止恶意文件入侵,是开发者必须面对的挑战。本文将围绕“基于Spring Boot 3.3 六种策略识别上传文件类型”这一主题,分享一些工作学习中的技术干货,帮助大家提升文件上传的安全性和效率。
661 0
|
XML JSON Java
spring,springBoot配置类型转化器Converter以及FastJsonHttpMessageConverter,StringHttpMessageConverter 使用
spring,springBoot配置类型转化器Converter以及FastJsonHttpMessageConverter,StringHttpMessageConverter 使用
1545 1
下一篇
oss云网关配置