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(); }