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;
    }
文章目录