问题描述
如题,在一次为修复流水线安全扫描漏洞进行依赖组件版本升级的过程中,出现生产环境升级jar版本后偶发NPE问题(由于版本发布包含其他需求的代码改动,多人协同敏捷开发,最初并不确定时jar升级导致的,经本地切换分支对比后得出结论是因为升级jar导致的问题)。该次jar版本升级情况为如下: 出现问题的是shiro-web v1.9.0-》v1.12.0
原因分析
- 代码在FilterConfig及ShiroConfig两个配置类中,将ShiroFilter注册了两次,通过调试信息可以看到filterChain中有两个ShiroFilter。一个在XssFilter之前(FilterConfig中定义)一个在靠后的位置(ShiroConfig中定义)。

- 查看源码可知ShiroFilter是OncePerRequestFilter类的子类

在shiro-1.9.0版本中,OncePerRequestFilter过滤器子类型默认只执行一次。切换到shiro-1.12.0后, 暴露filterOncePerRequest 属性,且值默认为false。



所以切换后,第二个ShiroFilter也执行了。
- 在执行第二个ShiroFilter时,已经执行过了XssFilter和SqlFilter。在requestheader中没有token或者token已经超时的情况下,会有如下调用栈:
# 在onAccessDenied()->getWrapper()->new ParameterRequestWrapper()->
public ParameterRequestWrapper(HttpServletRequest request) {
super(request);
//将参数表,赋予给当前的Map以便于持有request中的参数
this.params.putAll(request.getParameterMap());
}
# 此时,request.getParameterMap()会调用到SqlInjectHttpServletRequestWrapper类的getParameterMap()。
# SqlInjectHttpServletRequestWrapper#getParameterMap() -> sqlAntiInjectEncode() ->SqlUtil.sqlAntiInject(input);
public static String sqlAntiInject(String str){
if(StringUtils.isBlank(str)){
return null;//粗暴返回了null,因而第二次过oauth2过滤器时存在value=null的参数,导致某个未判空的校验逻辑出现空指针异常
}
xxxx
}
通过sqlAntiInject方法,将入参中 值为空字符的参数值转为了null
- 当Filter执行完,该正常执行读取参数的代码时,request中这类参数的值已经是null,再次调用到XssHttpServletRequestWrapper#的getParameterMap()方法时,其内部的调用栈中,会以参数的值创建类,存在某个未判空的校验逻辑,此时就会报出空指针异常了。
解决方案
方案一:优化完善原代码,避免多次注册相同filter,同时优化入参校验代码
-
将FilterConfig及ShiroConfig两个类中注册shiroFilter的代码合并到一个类中,即配置ShiroFilter,又管理filter的执行顺序;

-
在XssFilter及SqlFilter对参数处理的代码中加入对null的处理,加强代码健壮性
方案二: 显示设置filterOncePerRequest=true,以保证相同filter在一次请求中只执行一次,和升级前1.9.0的效果一致
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
ShiroFilterConfiguration configuration = new ShiroFilterConfiguration();
configuration.setFilterOncePerRequest(Boolean.TRUE);
shiroFilterFactoryBean.setShiroFilterConfiguration(configuration);
效果如下图:
2415

被折叠的 条评论
为什么被折叠?



