解决shiro1.10.0登录过滤器执行2次的问题

本来以为就是一个简单的版本升级, 改下pom重新打包梳理jar依赖替换下就可以, 结果发现登录请求里的createToken之类的方法每次都执行2次.
如果是普通登录请求倒也无所谓, 现在在登录过程中做了密码加密, 登录密码仅能够解析一次. 第一次进过滤器createToken解析成功, 第二次在执行createToken时解析临时密码就无效了, 所以到最后前端收到的信息都是密码无效.

这里发一下解决办法, 其实非常简单. 创建ShiroFilterFactoryBean的时候, 给他一个ShiroFilterConfiguration实例对象, 并且设置这个实例的setFilterOncePerRequest(true)即可解决问题.
贴一下代码:

ShiroFilterConfiguration config = new ShiroFilterConfiguration();
config.setFilterOncePerRequest(Boolean.TRUE);

ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setShiroFilterConfiguration(config);
// TODO: 这里放自定义过滤器
factoryBean.setFilters(filters);
// TODO: 这里放url过滤器映射规则
factoryBean.setFilterChainDefinitionMap(filterRuleMap);

上面说法解决办法, 下面说一下为啥突然需要这么搞才能解决问题.

跟一下ShiroFilterFactoryBean的代码, 发现本次setShiroFilterConfiguration之后, 这个对象主要用于下面这个方法

    private void applyGlobalPropertiesIfNecessary(Filter filter) {
        this.applyLoginUrlIfNecessary(filter);
        this.applySuccessUrlIfNecessary(filter);
        this.applyUnauthorizedUrlIfNecessary(filter);
        if (filter instanceof OncePerRequestFilter) {
            ((OncePerRequestFilter)filter).setFilterOncePerRequest(this.filterConfiguration.isFilterOncePerRequest());
        }

    }

而这个方法调用者就在下面

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Filter) {
            log.debug("Found filter chain candidate filter '{}'", beanName);
            Filter filter = (Filter)bean;
            this.applyGlobalPropertiesIfNecessary(filter);
            this.getFilters().put(beanName, filter);
        } else {
            log.trace("Ignoring non-Filter bean '{}'", beanName);
        }

        return bean;
    }

从方法名称上可以看出来postProcessBeforeInitialization方法在bean初始化的时候去执行, 将自定义的登录过滤器中的setFilterOncePerRequest设置为了ShiroFilterConfiguration实例中给定的true

再查一下1.9.1到1.10.0的升级内容 git:1.9.1>1.10.0差异对比

发现OncePerRequestFilter类中的核心方法

public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
        if ( request.getAttribute(alreadyFilteredAttributeName) != null && filterOncePerRequest) {
            log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());
            filterChain.doFilter(request, response);
        } else //noinspection deprecation
            if (/* added in 1.2: */ !isEnabled(request, response) ||
                /* retain backwards compatibility: */ shouldNotFilter(request) ) {
            log.debug("Filter '{}' is not enabled for the current request.  Proceeding without invoking this filter.",
                    getName());
            filterChain.doFilter(request, response);
        } else {
            // Do invoke this filter...
            log.trace("Filter '{}' not yet executed.  Executing now.", getName());
            request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

            try {
                doFilterInternal(request, response, filterChain);
            } finally {
                // Once the request has finished, we're done and we don't
                // need to mark as 'already filtered' any more.
                request.removeAttribute(alreadyFilteredAttributeName);
            }
        }
    }

上面代码第四行, 增加了&& filterOncePerRequest 判断, 这个值就是通过ShiroFilterConfiguration > ShiroFilterFactoryBean一路传进来的, 而且他是在构造ShiroFilterFactoryBean之后执行的, 比自定义Filter的构造时间要晚, 所以尝试在自定义过滤器的构造方法或者postxxx, afterxxx之类的方法中去设置为true都是没用的.

所以只能是构造ShiroFilterFactoryBean对象时, 设置其配置属性来解决问题.

也就是说以前OncePerRequestFilter过滤器子类型默认只执行一次, 现在不是了, 现在可以通过全局配置来选择是否启用OncePerRequestFilter的只执行一次机制.

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值