shiro1.7不允许URL路径中存在中文

有时候我们的URL是用动态模板生成的,比如

/article/{id}

此时id会由URL传入,动态匹配到此mapping中

有时候会涉及到中文,比如搜索

/search/key/{words}

words是可能传入中文的,而shiro1.7不允许URL路径中存在中文(允许参数为中文,不允许路劲中有中文),否则报status=400,参数映射错误

不允许:/search/key/文章
允许:/search/key?words=文章

这是shiro1.7后增加的一个路径ascii验证。

类InvalidRequestFilter

@Override
    protected boolean isAccessAllowed(ServletRequest req, ServletResponse response, Object mappedValue) throws Exception {
        HttpServletRequest request = WebUtils.toHttp(req);
        // check the original and decoded values
        return isValid(request.getRequestURI())      // user request string (not decoded)
                && isValid(request.getServletPath()) // decoded servlet part
                && isValid(request.getPathInfo());   // decoded path info (may be null)
    }
    private boolean isValid(String uri) {
        return !StringUtils.hasText(uri)
               || ( !containsSemicolon(uri)
                 && !containsBackslash(uri)
                 && !containsNonAsciiCharacters(uri));
    }
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        WebUtils.toHttp(response).sendError(400, "Invalid request");
        return false;
    }

isValid中由校验NonAsciiCharacters,如果url路径包含中文,此处会返回false,并抛出400错误。

解决方案,重写工厂bean,将此校验去掉

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.InvalidRequestFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.beans.factory.BeanInitializationException;
import javax.servlet.Filter;
import java.util.Map;
/**
 * 自定义ShiroFilterFactoryBean解决资源中文路径问题
 * AccessControlFilter.onPreHandle校验住了
 * 子类 InvalidRequestFilter重写了 isAccessAllowed
 * isValid方法校验 !containsNonAsciiCharacters
 * 将InvalidRequestFilter的blockNonAscii设置成false就不会校验
 */
public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean {
    @Override
    public Class<MySpringShiroFilter> getObjectType()
    {
        return MySpringShiroFilter.class;
    }
    @Override
    protected AbstractShiroFilter createInstance() throws Exception
    {
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null)
        {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }
        if (!(securityManager instanceof WebSecurityManager))
        {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }
        FilterChainManager manager = createFilterChainManager();
        // Expose the constructed FilterChainManager by first wrapping it in a
        // FilterChainResolver implementation. The AbstractShiroFilter implementations
        // do not know about FilterChainManagers - only resolvers:
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);
        Map<String, Filter> filterMap = manager.getFilters();
        Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name());
        if (invalidRequestFilter instanceof InvalidRequestFilter)
        {
            // 此处是关键,设置false跳过URL携带中文400,servletPath中文校验bug
            ((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);
        }
        // Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
        // FilterChainResolver. It doesn't matter that the instance is an anonymous inner class
        // here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
        // injection of the SecurityManager and FilterChainResolver:
        return new MySpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }
    private static final class MySpringShiroFilter extends AbstractShiroFilter
    {
        protected MySpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver)
        {
            if (webSecurityManager == null)
            {
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            }
            else
            {
                this.setSecurityManager(webSecurityManager);
                if (resolver != null)
                {
                    this.setFilterChainResolver(resolver);
                }
            }
        }
    }
}

关键是这行,可以去掉ascii的校验

((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);

然后shiroconfig配置类中,由原来实例化ShiroFilterFactoryBean,改成重写的类

@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
//ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//自定义ShiroFilterFactoryBean解决资源中文路径问题
ShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();
}


{context}