shiro最新版本 1.6.0登录bug修复

2020年8月17日,shiro发布了1.6.0版本,修复了绕过认证的bug。但实际升级后,使用中发现第一次打开浏览器访问时会出现Invalid request。具体错误提示如下:

降级到1.4.2时是可以正常登录的,反复试验几次,确定是升级shiro版本引起,阅读shiro 1.6.0源码,发现shiro引入了InvalidRequestFilter过滤器,目的是验证url上是否有恶意伪造的特殊字符。但这个类也正好将登录url拼接的

;jsessionId=xxx

也一起拦截了,结果就出现了上图中的错误提示。

InvalidRequestFilter的containsSemicolon()方法对url上的特殊符号“;”进行了拦截处理,但不知为何没有考虑到登录时拼接的jsessionId。

找到了问题点,解决起来就很简单,那就是改造containsSemicolon方法,对登录的url进行特殊处理放行。这里告诉大家个小秘密,那就是怎么修改别人jar包里面的代码。只需在自己项目的源码里面创建一个相同的class(包路径类名都必须相同)就可以了,然后将源代码全部复制粘贴过来,接下来就是修改源码。修改后的源码如下,大家可以直接复制使用。

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.shiro.web.filter;

import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * A request filter that blocks malicious requests. Invalid request will respond with a 400 response code.
 * <p>
 * This filter checks and blocks the request if the following characters are found in the request URI:
 * <ul>
 *     <li>Semicolon - can be disabled by setting {@code blockSemicolon = false}</li>
 *     <li>Backslash - can be disabled by setting {@code blockBackslash = false}</li>
 *     <li>Non-ASCII characters - can be disabled by setting {@code blockNonAscii = false}, the ability to disable this check will be removed in future version.</li>
 * </ul>
 *
 * @see <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/firewall/StrictHttpFirewall.html">This class was inspired by Spring Security StrictHttpFirewall</a>
 * @since 1.6
 */
public class InvalidRequestFilter extends AccessControlFilter {

    private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));

    private static final List<String> BACKSLASH = Collections.unmodifiableList(Arrays.asList("\", "%5c", "%5C"));

    private boolean blockSemicolon = true;

    private boolean blockBackslash = true;

    private boolean blockNonAscii = true;

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        String uri = WebUtils.toHttp(request).getRequestURI();
        return !containsSemicolon(request,uri)
            && !containsBackslash(uri)
            && !containsNonAsciiCharacters(uri);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    	String uri = WebUtils.toHttp(request).getRequestURI();
        WebUtils.toHttp(response).sendError(400, "Invalid request");
        return false;
    }

    private String ctx=null;
    private boolean containsSemicolon(ServletRequest request,String uri) {
        if (isBlockSemicolon()) {
        	if(ctx == null) {
        		ctx = WebUtils.toHttp(request).getContextPath();
        	}
        	// 登录url拼接的jsessionId进行放行
        	if(uri.startsWith(ctx + this.getLoginUrl() + ";jsessionid=") || 
        			uri.startsWith(ctx + this.getLoginUrl() + "%3bjsessionid=") || 
        			uri.startsWith(ctx + this.getLoginUrl() + "%3Bjsessionid=")) {
        		return false;
        	}
        	boolean value = SEMICOLON.stream().anyMatch(uri::contains);
        	return value;
        }
        return false;
    }

    private boolean containsBackslash(String uri) {
        if (isBlockBackslash()) {
            return BACKSLASH.stream().anyMatch(uri::contains);
        }
        return false;
    }

    private boolean containsNonAsciiCharacters(String uri) {
        if (isBlockNonAscii()) {
            return !containsOnlyPrintableAsciiCharacters(uri);
        }
        return false;
    }

    private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
        int length = uri.length();
        for (int i = 0; i < length; i++) {
            char c = uri.charAt(i);
            if (c < ' ' || c > '~') {
                return false;
            }
        }
        return true;
    }

    public boolean isBlockSemicolon() {
        return blockSemicolon;
    }

    public void setBlockSemicolon(boolean blockSemicolon) {
        this.blockSemicolon = blockSemicolon;
    }

    public boolean isBlockBackslash() {
        return blockBackslash;
    }

    public void setBlockBackslash(boolean blockBackslash) {
        this.blockBackslash = blockBackslash;
    }

    public boolean isBlockNonAscii() {
        return blockNonAscii;
    }

    public void setBlockNonAscii(boolean blockNonAscii) {
        this.blockNonAscii = blockNonAscii;
    }
}

shiro在浏览器第一次访问时会在url上拼接jsessionId到url后面,目的是确保cookie里面有sessionId以保持会话,但拼接时是用

;jsessionid=xxx

的形式拼接的,常规url后面拼接的都是“”和“&”,这是shiro过滤器处理的结果。

以上是我想到的解决办法,如果大家也遇到了这个问题并有更好的办法,希望能一起探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值