SpringBoot整合Shiro后中文Restful URL拦截的问题
SpringBoot整合Shiro后中文Restful URL拦截的问题
Springboot Restful 出现:Whitelabel Error Page 400错误
"timestamp": "2021-05-13 08:48:57",
"status": 400,
"error": "Bad Request",
"message": "Invalid request",
"path": "/books/name/%E5%9B%BD%E5%AE%B6/page/1/size/10"
}
经过一步一步的排查 发现是 Shiro过滤器的问题
即,Shiro在springboot中默认配置的全局过滤器 InvalidRequestFilter
,作用是拦截过滤非法字符的Url,这个过滤器会直接把中文字符是为非法,从而拦截请求 产生400 错误
详细内容参考 👉 ShiJh
重写AccessControlFilter
package cn.bookmanager.config;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
@Component
public class ShiroCNInvalidRequestFilter 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 = !Boolean.getBoolean("org.apache.shiro.web.ALLOW_BACKSLASH");
private boolean blockNonAscii = true;
@Override
protected boolean isAccessAllowed(ServletRequest req, ServletResponse response, Object mappedValue) throws Exception {
HttpServletRequest request = WebUtils.toHttp(req);
return this.isValid(request.getRequestURI()) && this.isValid(request.getServletPath()) && this.isValid(request.getPathInfo());
}
private boolean isValid(String uri) {
return !StringUtils.hasText(uri) || !this.containsSemicolon(uri) && !this.containsBackslash(uri) && !this.containsNonAsciiCharacters(uri);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
WebUtils.toHttp(response).sendError(400, "Invalid request");
return false;
}
private boolean containsSemicolon(String uri) {
if (this.isBlockSemicolon()) {
Stream<String> var10000 = SEMICOLON.stream();
Objects.requireNonNull(uri);
return var10000.anyMatch(uri::contains);
} else {
return false;
}
}
private boolean containsBackslash(String uri) {
if (this.isBlockBackslash()) {
Stream<String> var10000 = BACKSLASH.stream();
Objects.requireNonNull(uri);
return var10000.anyMatch(uri::contains);
} else {
return false;
}
}
private boolean containsNonAsciiCharacters(String uri) {
if (this.isBlockNonAscii()) {
return !containsOnlyPrintableAsciiCharacters(uri);
} else {
return false;
}
}
private static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
}
private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
int length = uri.length();
for(int i = 0; i < length; ++i) {
char c = uri.charAt(i);
// Do not use complicated statements in conditional statements
// if( (c < "" || c > "~" ) && !isChinese(c) )
if (c < " " || c > "~") {
if (!isChinese(c)){
return false;
}
}
}
return true;
}
public boolean isBlockSemicolon() {
return this.blockSemicolon;
}
public boolean isBlockBackslash() {
return this.blockBackslash;
}
public boolean isBlockNonAscii() {
return this.blockNonAscii;
}
}
添加到ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager,
ShiroCNInvalidRequestFilter invalidRequestFilter
) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
// *** 自己的规则
List<String> globalFilters = new LinkedList<>();
globalFilters.add("invalid");
bean.setGlobalFilters(globalFilters);
return bean;
}