Struts2.0一下,升级到Struts2.5的问题

  • Post author:
  • Post category:其他

朋友求助,老项目客户要求升级到Struts2.5.30,规避必要漏洞和风险。搞了一两周没进展。

1、Method XX from action XX is not allowed!

网上方法基本是在XML中进行配置,算基本配置吧,包括

1)package标签中增加方法通配符

<global-allowed-methods>regex:.*</global-allowed-methods>

2)package标签增加 strict-method-invocation=”false” 属性配置

<package name="security" namespace="/xframe/security" extends="XXXDefault" 
    strict-method-invocation="false">

这个属性Struts2.5默认是SMI为true,且不能全局设置,只能一个个Package进行设置,不申明的都不允许访问。然后就是配置在struts.xml中的action可以用了,但自动扫描的包都不行。

网上能搜到的基本就是这个配置,但怎么都不行。

在仔细研究。

2、codebehind插件,替换为struts2.3引入替换的新插件convention

接上条,仔细研究后,发现老项目用的codebehind插件,简化配置。该插件会自动扫描制定的源码包,把扫描出来的action和响应方法都暴露给外面允许访问。

这恰恰是Struts2.5安全性能提升到的一个点。于是换convertion。

convertion的主要配置如下:

<!-- 限制方法调用之方法前缀 -->
<constant name="struts.strictMethodInvocation.methodRegex" value="([A-Za-z]*)"/>
<!-- 进行扫描的根包,该包会被扫描成action -->
<constant name="struts.convention.action.packages" value="com.xx" />
<!-- 指定的包会被进行扫描 -->
<constant name="struts.convention.package.locators" value="action,actions,web" />
<!-- 返回页面地址 -->
<constant name="struts.convention.result.path" value="/pages/" />
<!-- 用于进行action查找的后缀 -->
<constant name="struts.convention.action.suffix" value="Action" />

然后,发现依旧是not allowed!

3、自定义convention扩展

卡了好长时间,最后下载了Struts2.5的全套源码,通过eclipse设置源码后开始调试。发现在请求的时候,convention自动扫描到的Action,在请求的时候,生成的ActionConfig中的actionMethod压根就没有Package的global-allowed-method允许的通配符。然后通过XML配置进来的action确有通配符。

convertion和XML不通!!!

有卡壳,继续研究,尤其是官方的文档和WIKI

Convention plugin (apache.org)https://struts.apache.org/plugins/convention/Convention Plugin – Apache Struts 2 Wiki – Apache Software Foundationhttps://cwiki.apache.org/confluence/display/WW/Convention+Plugin发现没有任何这方面的说明。没有!

然后是发现可以扩展convention,官方文档中的overwriting部分。

Convention plugin (apache.org)https://struts.apache.org/plugins/convention/#overwriting-plugin-classes最后的扩展如下:

1)配置,在Struts.xml中配置自己的

<bean type="org.apache.struts2.convention.ActionConfigBuilder" 
   name="convention1" class="com.xxx.security.PackageBasedActionConfigBuilderCustom"></bean>
<constant name="struts.convention.actionConfigBuilder" value="convention1"/>

发现convention的PackageBasedActionConfigBuilder本身代码也很多,1141行。就没有按说明实现interface,而是直接继承了PackageBasedActionConfigBuilder,然后多关键方法做覆盖。

2)PackageBasedActionConfigBuilderCustom类

2.1)构造方法

@Inject
public PackageBasedActionConfigBuilderCustom(Configuration configuration, Container container, ObjectFactory objectFactory,
  @Inject(ConventionConstants.CONVENTION_REDIRECT_TO_SLASH) String redirectToSlash,
  @Inject(ConventionConstants.CONVENTION_DEFAULT_PARENT_PACKAGE) String defaultParentPackage) {
   super(configuration, container, objectFactory, redirectToSlash,defaultParentPackage);
    // Validate that the parameters are okay
   this.configuration = configuration;
   this.actionNameBuilder = container.getInstance(ActionNameBuilder.class, container.getInstance(String.class, ConventionConstants.CONVENTION_ACTION_NAME_BUILDER));
   this.resultMapBuilder = container.getInstance(ResultMapBuilder.class, container.getInstance(String.class, ConventionConstants.CONVENTION_RESULT_MAP_BUILDER));
// this.interceptorMapBuilder = container.getInstance(InterceptorMapBuilder.class, container.getInstance(String.class, ConventionConstants.CONVENTION_INTERCEPTOR_MAP_BUILDER));
   this.objectFactory = objectFactory;

//        this.redirectToSlash = Boolean.parseBoolean(redirectToSlash);

   if (LOG.isTraceEnabled()) {
      LOG.trace("Setting action default parent package to [{}]", defaultParentPackage);
    }

//        this.defaultParentPackage = defaultParentPackage;
}

先调用父类的,其他变量是为了方便后面引用的。

2.2)createActionConfig

/**
 * Creates a single ActionConfig object.
 *
 * @param pkgCfg       The package the action configuration instance will belong to.
 * @param actionClass  The action class.
 * @param actionName   The name of the action.
 * @param actionMethod The method that the annotation was on (if the annotation is not null) or
 *                     the default method (execute).
 * @param annotation   The ActionName annotation that might override the action name and possibly
 */

protected void createActionConfig(PackageConfig.Builder pkgCfg, Class<?> actionClass, String actionName,
                                  String actionMethod, Action annotation, Set<String> allowedMethods) {
	
	Set<String> ss = new HashSet<>();
	ss.add("regex:.*");
	pkgCfg.addGlobalAllowedMethods(ss);

	super.createActionConfig(pkgCfg, actionClass, actionName, actionMethod, annotation, allowedMethods);

	//判断nameSpace空间是否严格限制方法调用,默认是不允许的。
	pkgCfg.strictMethodInvocation(false);
	
	packageNum++;
	
	LOG.debug(packageNum + "\t" + pkgCfg.getNamespace() + "\t" + actionName + "\t" + actionMethod + "\t" + actionClass.getName());
	
}

本方法一是给自动构建的package增加GlobalAllowedMethods通配符,二是关闭方法调用严格限制。

如此,Not Allowed终于解决了。

2.3)Could not find action or result

之后还发现一个问题,找不到XML中配置的全局Result

<global-results>
	<result name="error">/pages/xframe/global/Error.jsp</result>
	<result name="login-input" type="redirectAction">
		<param name="actionName">Login</param>
		<param name="method">input</param>
		<param name="namespace">/</param>
	</result>
</global-results>
Could not find action or result:/xxx/.action

Could not find action or result

全局设定,也是为了简化处理的。但在XML中配置的内容,和convention中还是不互通。

重新修改了上述方法如下。

protected void createActionConfig(PackageConfig.Builder pkgCfg, Class<?> actionClass, String actionName,
                                  String actionMethod, Action annotation, Set<String> allowedMethods) {

	//获取XML中的defaultParentPackage对应的GlobalResult
	for(String cfgName :this.configuration.getPackageConfigNames()){
		PackageConfig cfg  = this.configuration.getPackageConfig(cfgName);//XML中的defaultParentPackage
		Map<String, ResultConfig>  map = cfg.getGlobalResultConfigs();
    	if(map.size() > 0){
    		pkgCfg.addGlobalResultConfigs(map);
    	}
	}
	
	Set<String> ss = new HashSet<>();
	ss.add("regex:.*");
	pkgCfg.addGlobalAllowedMethods(ss);

	super.createActionConfig(pkgCfg, actionClass, actionName, actionMethod, annotation, allowedMethods);

	//判断nameSpace空间是否严格限制方法调用,默认是不允许的。
	pkgCfg.strictMethodInvocation(false);
	
	packageNum++;
	
	LOG.debug(packageNum + "\t" + pkgCfg.getNamespace() + "\t" + actionName + "\t" + actionMethod + "\t" + actionClass.getName());

}

至此,问题解决。

其他诸如Convention扫描出来的Package的Namespace不规范等,单独写一个方法重新生成Namespace和ActionName即可。


版权声明:本文为winderain原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。