【生产问题记录】shiro版本升级引起页面偶发NPE

问题描述

如题,在一次为修复流水线安全扫描漏洞进行依赖组件版本升级的过程中,出现生产环境升级jar版本后偶发NPE问题(由于版本发布包含其他需求的代码改动,多人协同敏捷开发,最初并不确定时jar升级导致的,经本地切换分支对比后得出结论是因为升级jar导致的问题)。该次jar版本升级情况为如下: 出现问题的是shiro-web v1.9.0-》v1.12.0jar升级情况

原因分析

  1. 代码在FilterConfig及ShiroConfig两个配置类中,将ShiroFilter注册了两次,通过调试信息可以看到filterChain中有两个ShiroFilter。一个在XssFilter之前(FilterConfig中定义)一个在靠后的位置(ShiroConfig中定义)。
    过滤器链
  2. 查看源码可知ShiroFilter是OncePerRequestFilter类的子类
    在这里插入图片描述
    在shiro-1.9.0版本中,OncePerRequestFilter过滤器子类型默认只执行一次。切换到shiro-1.12.0后, 暴露filterOncePerRequest 属性,且值默认为false。
    1.9.0shiro
    1.12.0shiro
    debug可知默认filterOncePerRequest=false

所以切换后,第二个ShiroFilter也执行了。

  1. 在执行第二个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

  1. 当Filter执行完,该正常执行读取参数的代码时,request中这类参数的值已经是null,再次调用到XssHttpServletRequestWrapper#的getParameterMap()方法时,其内部的调用栈中,会以参数的值创建类,存在某个未判空的校验逻辑,此时就会报出空指针异常了。

解决方案

方案一:优化完善原代码,避免多次注册相同filter,同时优化入参校验代码

  1. 将FilterConfig及ShiroConfig两个类中注册shiroFilter的代码合并到一个类中,即配置ShiroFilter,又管理filter的执行顺序;
    在这里插入图片描述

  2. 在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);

效果如下图:在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值