shiro最新版本 1.6.0登录bug修复

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

shiro最新版本 1.6.0登录bug修复_第1张图片

降级到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.
 * 

* This filter checks and blocks the request if the following characters are found in the request URI: *

    *
  • Semicolon - can be disabled by setting {@code blockSemicolon = false}
  • *
  • Backslash - can be disabled by setting {@code blockBackslash = false}
  • *
  • Non-ASCII characters - can be disabled by setting {@code blockNonAscii = false}, the ability to disable this check will be removed in future version.
  • *
* * @see This class was inspired by Spring Security StrictHttpFirewall * @since 1.6 */ public class InvalidRequestFilter extends AccessControlFilter { private static final List SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B")); private static final List 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 < '\u0020' || c > '\u007e') { 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过滤器处理的结果。

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

你可能感兴趣的:(java,shiro)