Struts2真是三天两头爆出漏洞来,特殊是一爆出漏洞就建议升级到最新版,也是无语的,并且最新版比较老板还相差挺大的,这部这次须要从Struts2.3.32一下子就必须升级到最新版,这可让人头大,谷歌百度了几天都没有办理方案,大概是由于每个人的项目都是不同的吧,有些用纯表明,有些用配置文件,以是可能适宜你的办理方案却不适宜别人,终极还是得靠自己办理,下面整理了一下我的办理方案,个中有些缺点只能通过修正源码来搞定的。
一、项目背景这里先要说一下要升级的项目背景,由于不同架构的项目升级方法可能有差异,我用的项目是Struts2.3.32,然后用纯表明动态方法调用的模式,也便是直接在Action上面加上要求的表明,然后每个方法都是要求路径,不须要修正配置文件,格式如下:
@ParentPackage(value = 34;default")@Namespace(value = "/")@Action(value = "indexAction",results = { @Result(name = "test",location = "/test.jsp"), @Result(name = "test2",location = "/test2.jsp")})public class IndexAction{ public String test(){ System.out.println("我是action,被test1调用"); return "test"; } public String test2(){ System.out.println("我是action,被test2调用"); return "test2"; }}
后面如果想要加要求也很大略,直接多加一个方法,然后多加一个@Result返回值就可以了。这个是我的项目构造,下面的升级步骤也是基于这个构造来的,不过就算大家的构造有差异,那么也该当有借鉴的地方,反正我基于的逻辑便是,实在是办理不了就从源码下手。
二、环境准备
我以为,升级之前的第一步便是自己用最新版的Struts2搭建一个项目,担保自己的升级包是可以用的先,然后在比拟自己项目的差异逐个攻破,以是我先自己搭建了一个例子:Strtus2.5.26动态方法调用表明模式环境搭建:https://www.suibibk.com/topic/788791049202958336
这里须要辞官网下载struts2.5.26的环境包和源码包:http://struts.apache.org/download.cgi#struts2526
struts-2.5.26-all.zip (65MB) [PGP] [SHA256]struts-2.5.26-src.zip (7MB) [PGP] [SHA256]
我本地搭建的时候貌似tomcat须要为tomcat8,但是在我的项目上貌似用apache-tomcat-7.0.96也可以,不过jdk彷佛就须要1.8,不然会报各种各样的缺点,得看每个人的详细情形。
三、Struts2.3.32升级到Struts2.5.26步骤1、修正web.xml在2.5.26后,struts的过滤器也改了一下,少了ng的包路径
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class></filter>
2、修正struts.xml
struts.xml的配置文件也须要修正下,比如主动开启动态方法调用,然后加上全局方法许可访问的配置,如下:
<constant name="struts.enable.DynamicMethodInvocation" value="true"/><constant name="struts.enable.SlashesInActionNames" value="true"/><constant name="struts.action.extension" value="do" /><package name="default" extends="struts-default"> ... <global-allowed-methods>regex:.</global-allowed-methods></package>
然后配置文件头部的版本也要改为2.5
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd">
把稳事变:注1:这里的package中的name一定要跟你Action中的@ParentPackage表明对应的一样我注2:我看网上还有些须要加上:strict-method-invocation=”false”,我升级创造并不须要
3、整理须要的jar包这里导入的jar包该当是你自己搭建测试的里面的jar包,也便是从struts-2.5.26\apps\struts2-showcase\WEB-INF\lib获取jar包,但是还要做如下处理
把spring开头的删除把tiles开头的删除4、删除原来的多余的jar包删除你原来项目中在你新准备的jar包中已经存在的版天职歧的jar包,也便是更换老的jar包,我这里就直接根据前缀来删除了,不过删除之前必须先备份,由于可能会多删除,那么须要把相应的jar包还原:
rm -rf strutsrm -rf asmrm -rf commonsrm -rf velocityrm -rf logrm -rf antlrrm -rf dom4j-1.6.1.jarrm -rf dwr.jar rm -rf freemarker-2.3.22.jarrm -rf xwork-core-2.3.32.jar
其实在后面的启动阶段就会报如下缺点:
报错1:Caused by: java.lang.NoClassDefFoundError: Lorg/apache/log4j/Logger;是由于新的log4j-api-2.12.1.jar包没有这个包路径,以是要把上面log4j-1.2.13.jar还原回去
报错2:找不到commons下面的http包以是要把commons-httpclient-3.0.jar还原回去
当然每个人的老项目都可能不一样,以是不一定会碰着这两个缺点。
5、上传jar包把你在第3步骤整理的jar包上传。
6、启动项目在启动之前,须要先准备一下,准备测试链接,我这里要做这一步是由于我们这个项目只有一个开拓环境,以是不能影响正常开拓,我就再nginx做了一个路径拦截,拦截某一起径然后转发到我这个运用下面。
如果启动报某些类找不到,那么该当是上面自己多删了,还原回去多删的就好,如果还有找不到的,就网上找到放进去就好,这些都不是问题,碰着的最最最棘手,最最最让人崩溃的问题是下面这个问题:
convention.annotation.Result.name()(Found data of type calss java.lang.String[index])
这个怎么搞,看了下@Result的源码,果真创造在以前的版本name是一个字符串,现在这个新的版本name是一个字符串数组,我的天,也便是说须要在表明上用如下模式?
@Result(name = {"test"},location = "/test.jsp"),
我看网上有一个人的办理方案便是把把项目中所有的name=”xxx”改为name={“xxx”},这当然可以办理,但是表明实在就算你哀求的是:
String[] name;
你还是可用name=”xxx”的模式的,以是这个缘故原由我们该当可以打消。然后网上说重新编译一下项目就好了,那当然,实在上面的改为{“xxxx”}的模式的方法该当也是重新编译一下项目就好了,但是我们的项目是在是太老了,重新编译不了,并且有些类跟生产环境的对不上,如果每个文件都要重新登记下版本重新取一下编译好的字节码class换莅临盆环境,可能性不大,并且我们的老项目本地丢启动不了,有些还有报错,以是这种办理方案我这边也用不了。
后面我想,得先看看现在的name解析的是什么,然后我就拷贝了一份struts2.5.26的源码,找到利用这个表明的地方,实在也便是在如下类中:
org/apache/struts2/convention/DefaultResultMapBuilder#createFromAnnotations
按相同的路径新建这个类然后修正下源码,打印出来:
for (Result result : results) { System.out.println("result:"+result); for (String name : result.name()) { ResultConfig config = createResultConfig(actionClass, new ResultInfo( name, result, packageConfig, resultPath, actionClass, resultsByExtension), packageConfig, result); if (config != null) { resultConfigs.put(config.getName(), config); }}
将编译后的文件放入项目的class目录下,重新启动,创造打印出的日志如下:
@org.apache.struts2.convention.annotation.Result(name=sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy@46e93c84, location=/chinapost/weixin/activitys/posterSharing/index.jsp, type=, params=[])
很明显name既不是字符串也不是数组,是一个非常工具:
sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy@46e93c84,
百度了下该非常:AnnotationTypeMismatchException非常。
public class AnnotationTypeMismatchExceptionextends RuntimeException若某个注释的类型在对该注释进行编译(或序列化)后发生了变动,而程序试图访问该注释的元素时,抛出此非常。
那该当是我们之前的源码编译是基于name为字符串的,现在直接不重新编译,更换jar包后,新的表明Result和DefaultResultMapBuilder类不能对之前的编译的类进行处理了,然后就报错了,那也阐明了为什么网上的办理方案都是重新编译就可以了,那我既然不能重新编译怎么办呢,办理方案如下:重写Result和DefaultResultMapBuilder。
7、重写Result和DefaultResultMapBuilder重写org.apache.struts2.convention.annotation.Result表明和org.apache.struts2.convention.DefaultResultMapBuilder
Result将String[] name 改为String name
DefaultResultMapBuilder的方法createFromAnnotations for循环改为直接取
for (Result result : results) { System.out.println("result:"+result); String name = result.name(); // for (String name : result.name()) { ResultConfig config = createResultConfig(actionClass, new ResultInfo( name, result, packageConfig, resultPath, actionClass, resultsByExtension), packageConfig, result); if (config != null) { resultConfigs.put(config.getName(), config); } // } }
然后项目可以启动了,打印出来的Result的name也是一个字符串啦。也就相称于有如下两个办理方案
办理方案1:重新编译项目即可办理方案2:不能重新编译项目则重写Result和DefaultResultMapBuilder,兼容以前的模式。
注:目前不清楚改了这两个地方是否有太大影响,但是知道的是name不能用数组的模式,必须按以前的单个值的模式开拓。
8、访问测试访问创造报如下缺点
Exception occurred during processing request: javax.servlet.ServletException: java.lang.NoSuchMethodError: org.apache.struts2.views.jsp.PropertyTag.setEscape(Z)Vorg.apache.jasper.JasperException: javax.servlet.ServletException: java.lang.NoSuchMethodError: org.apache.struts2.views.jsp.PropertyTag.setEscape(Z)V
这个是jsp报错,由于最新版本有一些标签页变了,这个要相应的改一下:如果你有这样的代码
<s:url id="url" action="login"><s:set id="str1" value="'string1 value'" /><s:property escape="true" var="someProperty"/>
须要改成:
<s:url var="url" action="login"><s:set var="str1" value="'string1 value'" /><s:property escapeHtml="true" var="someProperty"/>
然后再访问就成功进入啦。
四、总结1、升级步骤修正web.xml,将过滤器改为最新版本修正struts.xml 主动开启动态方法调用更换最新版本的jar包若可以重新编译项目,则直接重新编译项目若不可重新编译项目,则修正对应源码做兼容处理这里修正的额源码为:Result和DefaultResultMapBuilder修正JSP相应标签2、遗留问题须要对所有jsp进行检讨看看是否须要改标签还未对是否兼容spring做检讨,由于我们这个项目是直接调用微做事来处理的,不过也用了spring,还须要连续测试看看是否会有问题源码的修正为验证是否对其他功能有影响,唯一知道的是@Result中name不能用数组name{“xxxx”}模式,必须按以前的字符串模式name=”xxxx”待续…