浅谈Struts2请求解析过程

news2024/9/21 22:56:30

0x00前言

在使用Struts2的时候需要在web.xml中配置一个过滤器,来拦截用户发起的请求,并进行一些预处理,根据配置文件把请求分配给对应的action并将请求中的参数与action中的字段进行对应赋值。例如下面的例子,通过配置StrutsPrepareAndExecuteFilter过滤器对 Struts2所有的请求进行处理:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

StrutsPrepareAndExecuteFilter是一个Servlet过滤器,它是Struts 2框架的核心组件之一。它起到了连接Servlet容器和Struts 2框架的桥梁作用。它负责拦截请求,将请求交给框架处理,并将处理结果返回给Servlet容器,从而完成整个请求-响应周期。

0x01 Struts2请求解析过程

从StrutsPrepareAndExecuteFilter出发,查看具体的解析过程。

1.1 关键解析类

1.1.1 StrutsPrepareAndExecuteFilter

以struts2-core-2.5.25为例,查看StrutsPrepareAndExecuteFilter具体的工作流程:

可以看到其实现了Filter接口,而在过滤器中,doFilter方法是实际处理请求的核心方法。它被用于处理进入过滤器的请求,并在需要的情况下对请求进行修改或拦截,查看具体的实现:

image.png

在doFilter中,首先获取到了HttpServletRequest和HttpServletResponse对象,以便后续对请求和响应进行处理,这里通过调用org.apache.struts2.RequestUtils#getUri获取当前请求的uri,主要是用于日志输出的:

image.png

然后会检查是否有排除的URL模式,并且请求是否被排除在Struts的处理范围之外。如果是排除的URL,日志记录一条消息,并将请求传递给Filter链中的下一个Filter进行处理:

image.png

如果请求不是排除的URL,代码检查请求的URI是否是一个静态资源。如果是静态资源,将执行相应的处理,并将handled标记设置为true:

image.png

判断逻辑主要如下,首先通过HttpServletRequest对象获取到资源路径(resourcePath)。如果资源路径为空字符串,并且request.getPathInfo()不为空,则将resourcePath设置为request.getPathInfo(),然后获取StaticContentLoader实例(它是Struts2容器的一个组件,用于加载和处理静态资源)。检查StaticContentLoader是否能够处理给定的资源路径(resourcePath)。如果能够处理,调用staticResourceLoader的findStaticResource方法查找并响应静态资源,并返回true。如果StaticContentLoader无法处理资源路径,返回false:

image.png

如果请求不是静态资源,代码将进行更进一步的处理。首先,设置请求的编码和区域设置,并创建ActionContext。然后,为当前线程分配一个Dispatcher。接着,对请求进行包装和准备操作。之后,查找请求的ActionMapping,如果找不到Mapping,则将请求传递给Filter链中的下一个Filter。如果找到了Mapping,则执行对应的Action:

image.png

最后,无论是否找到Mapping,都会执行请求的清理操作:

image.png

以上是StrutsPrepareAndExecuteFilter的处理流程,主要时根据请求的URI和Struts的配置,判断请求是静态资源还是需要执行相应的Action,并对请求进行处理。也是Struts框架中请求处理的核心部分。

1.1.2 RequestUtils

在查找请求对应的ActionMapping进行处理时,主要是获取该URI匹配的ActionMapping配置,而请求的URI主要是在用org.apache.struts2.RequestUtils#getUri获取的:

image.png

查看org.apache.struts2.RequestUtils#getUri的具体实现:

首先,尝试从request对象的属性中获取javax.servlet.include.servlet_path属性值作为URI路径,如果uri不为null,则直接返回该uri:

image.png

如果uri为null,则调用getServletPath方法获取请求的servlet路径(uri)。非空字符串则直接返回,否则调用request.getRequestURI()方法获取完整的请求URI路径,然后通过截取字符串的方式,去除请求的上下文路径(request.getContextPath().length()),得到相对于上下文路径的URI路径并返回:

image.png

查看org.apache.struts2.RequestUtils#getServletPath的具体实现:

首先,通过request.getServletPath()和request.getRequestURI()方法获取对应的路径信息。然后进行一系列判断和处理:

image.png

若requestUri不以servletPath结尾,说明servlet路径与请求URI路径不完全匹配。此时,通过查找servletPath在requestUri中的起始位置,截取出匹配的部分作为新的servlet路径:

image.png

经过上述处理后,若servletPath不为空,则直接返回该servletPath,否则进行进一步处理。首先,确定startIndex为请求上下文路径(request.getContextPath())的长度。然后,根据request.getPathInfo()是否为null,确定endIndex的值。最后,通过截取字符串的方式,从requestUri中获取相对于上下文路径的servlet路径:

image.png

1.2 关键流程

1.2.1 寻找ActionMapping

在StrutsPrepareAndExecuteFilter中,如果请求不是静态资源,会对请求进行包装和准备操作。并查找请求对应的ActionMapping进行处理:

image.png

核心解析是在org.apache.struts2.dispatcher.PrepareOperations#findActionMapping中进行的,首先尝试在请求中查找对应的ActionMapping对象,如果找到了缓存的对象则直接返回,否则通过ActionMapper来获取,并将获取到的对象进行缓存。这样可以避免多次查找ActionMapping对象,提高性能:

image.png

通过ActionMapper来获取时,主要是调用org.apache.struts2.dispatcher.mapper.DefaultActionMapper#getMapping方法进行处理。查看具体的处理过程。

首先,会创建一个ActionMapping对象,用于保存请求对应的Action的相关信息,然后通过RequestUtils.getUri(request)方法获取请求的URI路径:

image.png

然后会对获取到的URI路径进行一定的处理,若URI路径中包含分号的索引,说明URI路径中包含附加信息,需要将其截取掉,只保留分号之前的部分。然后调用dropExtension方法,将URI路径中的扩展名部分去除,得到最终的URI路径:

image.png

经过上述处理后,若最终的URI路径为null,表示未找到对应的Action并返回null。

如果最终的URI路径不为null,继续执行以下操作:

  • 调用parseNameAndNamespace方法,解析URI路径中的名称和命名空间,并将解析结果保存到ActionMapping对象中
  • 调用handleSpecialParameters方法,处理特殊的请求参数,如路径参数等,并更新ActionMapping对象
  • 调用parseActionName方法,解析Action名称,并更新ActionMapping对象

image.png

最终返回封装后的ActionMapping对象。

1.2.2 缺省后缀

默认情况下,如用户请求路径不带后缀或者后缀以.action结尾都是可以解析的。

主要是在org/apache/struts2/default.properties配置文件定义的:

image.png

在dropExtension方法处下断点进行映证,可以看到默认的后缀跟default.properties定义是一致的:

image.png

也可以在struts.xml中添加常量struts.action.extension修改默认处理后缀:

1.2.3 parseNameAndNamespace解析

Struts2站点的URL路径主要由这三部分组成:namespace+Action+extension扩展。如下图所示:

image.png

在寻找ActionMapping时,会调用parseNameAndNamespace方法,解析URI路径中的名称和命名空间,找到对应的执行类。分析具体的解析过程。

首先找到URI中最后一个斜杠的位置并赋值给lastSlash变量,如果没有斜杠,将namespace设为默认值(空字符串),name设为请求的URI:

image.png

如果斜杠在第一个位置,将namespace设为根命名空间("/"),name为斜杠之后的部分:

image.png

如果alwaysSelectFullNamespace为true,则该代码块将URI中的整个部分作为namespace,将斜杠后面的部分作为名称。这意味着无论URI中是否存在其他命名空间,都会将整个URI作为当前Action的命名空间:

image.png

如果上述情况均不满足,则根据配置管理器的信息来确定命名空间,首先获取 configManager 中的配置对象 config。 然后通过 uri.substring(0, lastSlash) 将 uri 中的从开头到最后一个斜杠之前的部分提取为 prefix,通过迭代 config 中的所有package配置对象,获取每个package配置对象的namespace:

image.png

在迭代过程中,通过判断 prefix 是否以 ns 开头,并且判断 prefix 的长度是否与 ns 的长度相等,或者判断 prefix 的 ns 长度后的下一个字符是否为斜杠,来确定是否将当前的 ns 设为当前的命名空间,同时,如果 ns 是根路径 /,则将 rootAvailable 设为true:

image.png

最后通过将uri的namespace部分去除获取name,如果rootAvailable为true且 namespace 为空字符串,则将 namespace 的值设置为根路径 /:

image.png

处理完后,根据配置的规则,会根据allowSlashesInActionNames属性对namespace和name的格式进行处理,以便能正确匹配Action配置。

若allowSlashesInActionNames为false,会处理name中的斜杠。如果Action名称中存在斜杠,且斜杠不是名称的最后一个字符,则代码会将斜杠后面的部分作为新的name:

image.png

最后,将解析得到的命名空间和名称设置到ActionMapping对象中,整个匹配过程完成:

image.png

1.3 其他

1.3.1 FilterDispatcher

FilterDispatcher是struts2.0.x到2.1.2版本的核心过滤器,从2.1.3开始StrutsPrepareAndExecuteFilter就替代了FilterDispatcher:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

以struts2-core-2.2.3为例,查看FilterDispatcher具体的工作流程:

同样实现了Filter接口,在doFilter中,同样的首先获取到了HttpServletRequest和HttpServletResponse对象,以便后续对请求和响应进行处理:

image.png

然后创建一个ValueStack对象,用于管理action和视图中使用的值并设置ActionContext并关联上述创建的ValueStack。将计时器键值推入UtilTimerStack中,方便测量执行时间。然后调用repareDispatcherAndWrapRequest方法对request请求进行必要的包装处理:

image.png

然后尝试获取请求对应的ActionMapping,这里逻辑是类似的:

image.png

如果找到了ActionMapping,则通过Dispatcher调用相应的方法来处理Action,如果没有找到ActionMapping,它会检查请求的资源是否是静态资源,并由staticResourceLoader处理。如果是静态资源,将找到并返回该资源,否则会将请求传递给过滤器链中的下一个过滤器进行处理:

image.png

最后跟StrutsPrepareAndExecuteFilter一样,会执行请求的清理操作:

image.png

0x02 区分Spring与Struts2

根据前面的分析,可以简单的通过下面的方法对Struts2和Spring进行区分,SpringWeb的解析过程可以参考https://forum.butian.net/share/2214 。

2.1 缺省后缀

Struts2在默认情况下,如用户请求路径不带后缀或者后缀以.action结尾都是可以解析的,而Spring没有类似的特性。

可以看到默认情况下不带后缀或者后缀以.action结尾均可正常访问:

image.png

image.png

但是Spring中的SuffixPatternMatch模式启用时能以 .xxx 结尾的方式进行匹配。例如/hello和/hello.do的匹配结果是一样的。

2.2 路径匹配容错机制

根据前面的分析,Struts2在获取namespace时逻辑如下:

通过判断 prefix 是否以 ns 开头,并且判断 prefix 的长度是否与 ns 的长度相等,或者判断 prefix 的 ns 长度后的下一个字符是否为斜杠,来确定是否将当前的 ns 设为当前的命名空间,同时,如果 ns 是根路径 /,则将 rootAvailable 设为true:

image.png

也就是说可以在对应的namespace后加入任意的目录,均可以找到对应的Action:

image.png

而Spring明显是没有对应的特性的。

2.3 路径规范化的区别

Struts2在解析时对类似../是进行了处理的,如果requestUri中不包含servletPath且request.getServletPath返回不为null的话会直接返回:

image.png

所以如下请求可以成功访问到对应的action:

image.png

当Spring Boot版本在小于等于2.3.0.RELEASE时,会对路径进行规范化处理,但是高版本的getPathWithinApplication是通过request.getRequestURI()方法获取当前request中的URI/URL,并不会对获取到的内容进行规范化处理。当请求路径中包括类似..的关键词时,调用getPathWithinApplication方法解析后,会因为没有处理跨目录的字符,导致找不到对应的Handler而返回404。

2.4 尾部/的区别

在Spring中,当启用后缀匹配模式时,例如/hello和/hello.do的匹配结果是一样的:

image.png

同样的,当使用PathPattern进行解析时,在最后会根据matchOptionalTrailingSeparator(此参数为true时,默认为true)进行一定的处理,如果Pattern尾部没有斜杠,请求路径有尾部斜杠也能成功匹配(类似TrailingSlashMatch的作用):

image.png

而相比Struts2,在获取ActionName的时候,并没有考虑尾部额外/的问题,所以当尝试追加/时,Struts2可能会因为找不到对应的Action返回404:

image.png

2.5 Struts2特有的静态资源

除此以外,在Struts2的jar包中包含很多特有静态文件,可以利用这一点进行判断,例如struts2-core-2.5.25:

image.png

那么可以尝试在URL的Web应用根目录下添加/struts/domTT.css,如果返回css代码,大概率是Struts2:

image.png

同样的,类似低版本的同样会有特定的文件,例如struts2-core-2.0.1:

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/824445.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树的前,中,后序的非递归实现(c++)

前言 对于二叉树来说&#xff0c;遍历它有多种方式&#xff0c;其中递归遍历是比较简单的&#xff0c;但是非递归的实现就有一定的难度&#xff0c;在这里介绍一种非递归实现二叉树遍历的方式。 1.前序遍历 1.1思路 其实对于二叉树的非递归实现&#xff0c;实际上就是用代码来…

Spring中Bean的实例化详细流程

还是举个例子&#xff0c;我有一个朋友小汪他远赴南方某城市打工。然后安定下来后他的朋友很想来家里玩&#xff0c;但是呢我这个朋友家里搞的很乱&#xff0c;所以他不好意思请朋友来家里玩。这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了&#xff0c;然后给他…

【LeetCode】一、链表反转

题目 题目&#xff1a;给定单链表头节点&#xff0c;将单链表的链接顺序反转过来 例&#xff1a; 输入&#xff1a;1->2->3->4->5 输出&#xff1a;5->4->3->2->1 要求&#xff1a;按照两种方式实现 解决办法 方式一&#xff1a;&#xff08;直接迭…

腾讯云服务器CVM镜像操作系统大全_win_linux

腾讯云CVM服务器的公共镜像是由腾讯云官方提供的镜像&#xff0c;公共镜像包含基础操作系统和腾讯云提供的初始化组件&#xff0c;公共镜像分为Windows和Linux两大类操作系统&#xff0c;如TencentOS Server、Windows Server、OpenCloudOS、CentOS Stream、CentOS、Ubuntu、Deb…

大一python编程题库和答案,大一python基础编程题库

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;大一python编程题库和答案山东理工大学&#xff0c;大一python编程题库和答案解析&#xff0c;今天让我们一起来看看吧&#xff01; 单项选择题 第一章python语法基础 1. Python 3.x 版本的保留字总数是C A 27 …

Python(五十七)列表生成式

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

Delphi 开发不一样的窗体标题栏:TTitleBarPanel

目录 TTitleBarPanel 的使用 TTitleBarPanel 的使用进阶 一、设置标题栏高度、颜色 二、个性化标题栏的关闭等按键 我们在用Delphi开发程序的时候&#xff0c;窗体的标题栏一般都是标准的windows标题栏&#xff0c;上面包括&#xff1a;程序图标、标题、最小化、最大化、关闭…

TypeC拓展设计方案|TypeC转HDMI设计方案|CS5261/CS5265芯片设计参数对比

集睿智远CS5261/CS5265都可以用于设计TypeC转HDMI方案&#xff0c;低成本TypeC扩展坞设计方案&#xff0c;而两者也有些差异&#xff1a;1.CS5261支持DP1.4输入&#xff0c;一个HDMI1.4输出&#xff0c;即HDMI输出为4K30HZ ;CS5265DP1.4到HDMI2.0转换芯片&#xff0c;即HDMI输出…

Linux之 环境变量

什么是环境变量 windows中也有个 Linux 环境变量 env linux和windows环境变量&#xff0c;功能类似的&#xff0c; windows系统的环境变量&#xff0c;在cmd中可以之间调用程序运行。这些程序的执行程序的路径&#xff0c;一般编辑在path变量中 环境变量都分全局的&#xff…

Android性能优化—ANR问题分析

一、ANR是什么&#xff1f; ANR(Application Not responding)&#xff0c;是指应用程序未响应&#xff0c;Android系统对于一些事件需要在一定的时间范围内完成&#xff0c;如果超过预定时间能未能得到有效响应或者响应时间过长&#xff0c;都会造成ANR。可以简单的理解为应用…

网络入侵探测器Pi.Alert

什么是 Pi.Alert &#xff1f; Pi.Alert 是 WIFI/LAN 入侵探测器。通过扫描连接到您的 WIFI/LAN 的设备&#xff0c;提醒您未知设备的连接。它还警告断开“始终连接”的设备。 Pi.Alert 使用了三种扫描方式 方式1&#xff1a;arp-scan。arp扫描系统实用程序用于使用 arp 帧搜索…

【雕爷学编程】MicroPython动手做(28)——物联网之Yeelight 3

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

CommunityToolkit.Mvvm8.1 viewmodel使用-旧式写法

0.说明 CommunityToolkit.Mvvm8.1有一个重大更新的功能:源生成器功能,它极大简化我们的mvvm代码 但是本篇先总结一下原写法,下篇再总结源生成器功能 1.模型定义 必须继承:ObservableObject 2.viewmodel代码实现 几个关键点: SetProperty是给属性赋值,并且通知更改通知 But…

钉钉对接打通金蝶云星空获取流程实例列表详情(宜搭)接口与其他应收单接口

钉钉对接打通金蝶云星空获取流程实例列表详情&#xff08;宜搭&#xff09;接口与其他应收单接口 对接系统钉钉 钉钉&#xff08;DingTalk&#xff09;是阿里巴巴集团专为中国企业打造的免费沟通和协同的多端平台&#xff0c;提供PC版&#xff0c;Web版和手机版&#xff0c;有考…

Linux知识点 -- VS Code远程连接服务器协助开发

Linux知识点 – VS Code远程连接服务器协助开发 文章目录 Linux知识点 -- VS Code远程连接服务器协助开发一、VS Code的使用1.使用VS Code进行C语言编译与运行2.使用VS Code进行C代码的编译与运行 二、使用VS Code连接云服务器三、使用VS Code进行GDB调试 一、VS Code的使用 1…

Redis的基础知识

目录 一、什么是Redis 二、关于Redis的一些基本知识 &#xff08;1&#xff09;set命令 &#xff08;2&#xff09;get命令 三、Redis中的一些常用命令 &#xff08;1&#xff09;keys &#xff08;2&#xff09;exists &#xff08;3&#xff09;type &#xff08;4…

vite+typescript项目 :找不到模块“./***.vue”或其相应的类型声明——解决方案

vue3ts报错&#xff1a; 找不到模块“./App.vue”或其相应的类型声明。ts(2307) 解决方法&#xff1a; 1、在src文件夹找到 vite-env.d.ts 加入以下代码&#xff1a; declare module *.vue {import type { DefineComponent } from vueconst vueComponent: DefineComponent<…

Nodejs 第六章(npx)

npx是什么 npx是一个命令行工具&#xff0c;它是npm 5.2.0版本中新增的功能。它允许用户在不安装全局包的情况下&#xff0c;运行已安装在本地项目中的包或者远程仓库中的包。 npx的作用是在命令行中运行node包中的可执行文件&#xff0c;而不需要全局安装这些包。这可以使开…

手机python编程软件怎么用,手机python编程软件下载

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;手机python编程软件保存的代码在哪里&#xff0c;手机python编程软件怎么运行&#xff0c;现在让我们一起来看看吧&#xff01; 原标题&#xff1a;盘点几个在手机上可以用来学习编程的软件 前天在悟空问答的时候&#…

win上打包发包发布

地址 192.168.X.X 账号密码 zhanghaomima 连接方式&#xff1a; Win自带工具&#xff1a; 远程桌面连接 更新客户端代码 直接替换 D:\xmes\client3\elements 下的 cust-bundles 文件夹 更新mobile代码 直接替换 D:\xmes\mobile\scripts 下的fragments文件夹 更新服务端代…