如何利用AOP/Aspect来修改方法的入参

问题描述:
最近项目代码过三方测试(国企项目),在一系列代码扫描审计检查下,代码发现一部分修改,例如请求参数发生了编码/加密,导致后台需要对请求的参数进行解码/解密,后端那么接口,总不能挨个,挨个的去修改。

由于之前项目中,已经用了AOP进行代码日志的记录,日志记录如下
在这里插入图片描述
原本代码的核心逻辑如下:因此想着,既然这里已经拿到请求参数了,直接在这里统一解码/加密,就不用对每个接口解码了

        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        methodApiOperation = ms.getMethod().getDeclaredAnnotation(ApiOperation.class);
        if (methodApiOperation != null) {
        	apiOperationDes = methodApiOperation.value();
        }
        logger.info("start-->请求{}模块的[{}]服务",apiDes, apiOperationDes);
        logger.info("  请求地址:{}",url);
        logger.info("  请求方法:{}.{}", abbreviateName(joinPoint.getSignature().getDeclaringTypeName()), methodName);
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
        	if( !(args[i] instanceof StatHttpServletResponseWrapper)) {
        		logger.info("  请求参数{}:{}",i+1,JsonUtils.javaBeanToString(args[i]));
        	}
        }

修改思路1(利用Json格式化)(失败)

这里的请求参数是args 是Object类型,但是原始方法的请求类型肯定是各种自定的VO类
以解码html为例,我这里首先将Object[]挨个元素转成字符串,然后对整个字符串做html解码,将加码后的字符串,在创建一个新的对象赋joinPoint.getArgs的参数。结果发现修改并没有成功。

DecodeURL decodeURL = ms.getMethod().getDeclaredAnnotation(DecodeURL.class);
if(decodeURL != null) {
	Object[] args = joinPoint.getArgs();
	for (int i = 0; i < args.length; i++) {
	if( !(args[i] instanceof StatHttpServletResponseWrapper)) {
		String decode = StringEscapeUtils.unescapeHtml(JsonUtils.javaBeanToString(args[i]));
		logger.info("  解码Html请求参数{}",decode);
		//修改请求入参
		joinPoint.getArgs()[i] = JsonUtils.stringToJavaBean(decode, args[i].getClass());
		}
	}
}

代码执行逻辑是:

  1. 切面中记录请求参数-----》原始文本
  2. 切面中修改请求参数-----》修改后的文本
  3. 实际请求的controller-----》原始文本(也就是修改没有生效)
  4. 切面中调用请求参数-----》修改后的文本
    在这里插入图片描述

修改思路2(原始对象Set值)(有效,但没意义)

思路1中,我们是重新创建一个请求入参,然后把新的请求入参赋值给原始请求入参(Json格式化返回新的对象)
思路2,我们直接在原始的对象进行set值

DecodeURL decodeURL = ms.getMethod().getDeclaredAnnotation(DecodeURL.class);
if(decodeURL != null) {
	for (int i = 0; i < args.length; i++) {
	/*if( !(args[i] instanceof StatHttpServletResponseWrapper)) {
		String decode = StringEscapeUtils.unescapeHtml(JsonUtils.javaBeanToString(args[i]));
		logger.info("  解码Html请求参数{}",decode);
		//修改请求入参
		joinPoint.getArgs()[i] = JsonUtils.stringToJavaBean(decode, args[i].getClass());
	}*/
		if( (args[i] instanceof QueryResult)) {
			QueryResult query = (QueryResult)args[i];
			//修改请求入参
			query.setName(StringEscapeUtils.unescapeHtml(query.getName()));
		}
	}
}

此时查看实际的请求接口,发现值真的被修改了。
在这里插入图片描述
但是这里有个问题,上诉我是通过直接指定类型,然后强转类型,接着调用原对象的set方法,这是是我已知具体类型,具体字段,这样修改,我还不如直接找到原始的接口,在原来的接口里面修改。
AOP

QueryResult query = (QueryResult)args[i];
query.setName(StringEscapeUtils.unescapeHtml(query.getName()));

实际接口

@GetMapping
@DecodeURL
public void exportNxauto(HttpServletResponse response, QueryResult queryResult) {
	queryResult.setName(StringEscapeUtils.unescapeHtml(queryResult.getName()));
}

修改思路3(反射)

总结下思路1,思路1不用类型转换,也不用指定属性,格式化整个Json,然后对整个Json进行中文解码,但是转Json以后,导致重新创建了一个对象。思路2里面虽然没有创建新的对象,但是需要我们强制转化为某个类型,然后调用某个方式,实际请用场景,每个接口的入参的类型都不一样,具体是那个参数需要解码,所以也不知道调用那个Set方法。

Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
	if( !(args[i] instanceof StatHttpServletResponseWrapper)) {
    	logger.info("  请求参数{}:{}",i+1,JsonUtils.javaBeanToString(args[i]));
    }
}
ecodeURL decodeURL = ms.getMethod().getDeclaredAnnotation(DecodeURL.class);
if(decodeURL != null) {
	for (int i = 0; i < args.length; i++) {
		/*if( !(args[i] instanceof StatHttpServletResponseWrapper)) {
			String decode = StringEscapeUtils.unescapeHtml(JsonUtils.javaBeanToString(args[i]));
			logger.info("  解码Html请求参数{}",decode);
			//修改请求入参 失败:这里创建了一个新的对象,原始对象没有修改,修改的是新的对象。
			joinPoint.getArgs()[i] = JsonUtils.stringToJavaBean(decode, args[i].getClass());
		}*/
		/*if( (args[i] instanceof QueryResult)) {
			QueryResult query = (QueryResult)args[i];
			//修改请求入参 修改成功,但是太过于狭义,需要知道类型和具体的属性,然后调用Set方法
			query.setName();
		}*/
		if( !(args[i] instanceof StatHttpServletResponseWrapper)) {
			Class<? extends Object> classz = args[i].getClass();
			//使用反射改成功
			for (Field field : classz.getDeclaredFields()) {
				if(field.getType() == String.class) {
					ReflectionUtils.makeAccessible(field);
					Object value = field.get(args[i]);
					if(value != null) {
						ReflectionUtils.setField(field, args[i], StringEscapeUtils.unescapeHtml(value.toString()));
					}
				}else if (Collection.class.isAssignableFrom(field.getType())) {
				    // 字段是集合类型
				}else if (List.class.isAssignableFrom(field.getType())) {
				    // 字段是List类型
				}else if (field.getType().isArray()) {
				    // 字段是数组类型
				}else if (field.getType().isPrimitive()) {
				    // 字段是基本类型
				}
			}
		}
	}
}

接下来,我们看下运行的日志,可以看到,在我们的实际controller接口中,可以看到字符串类型的已经被html进行解码。
在这里插入图片描述

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值