Problem
When I'm using shiro in my springboot blog project, if URL contains Chinese characters, e.g. /search/搜索文本
, the response will be 400(Bad Request).
很坑哦,使用Springboot的Shiro框架1.7以上版本会有这样的问题,即当URL 请求包含中文字符,传入后台时,会报400错误。
Reason
Shiro 1.7 + blocks non-ASCII paths
Shiro 1.7以上会阻止非ASCII字符的路径。
Solution
STEP 1
Write my own ShiroFilterFactoryBean
, where I need also define MySpringShiroFilter
.
自己重写一个ShiroFilterFactoryBean
,在其中自定义一个自己的过滤器。
public class CustomShiroFilaterFactoryBean extends ShiroFilterFactoryBean {
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);
}
}
}
}
@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
// 后续版本可能再也不能设置这个了,因为setBlockNonAscii方法可能要作废
((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);
}
}
STEP 2
Configure my filter factory bean in ShiroConfig
.
把上述定义好的自定义工厂设置到ShiroConfig
类中。
//ShiroFilter过滤所有请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
// 自己定义的那个filter factory bean
CustomShiroFilaterFactoryBean shiroFilterFactoryBean = new CustomShiroFilaterFactoryBean();
//给ShiroFilter配置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new LinkedHashMap<>();
// 配置不会被拦截的链接 顺序判断
map.put("/static/**", "anon");
map.put("/yecgaa/login", "anon");
map.put("/yecgaa/dist/**", "anon");
map.put("/yecgaa/plugins/**", "anon");
// 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
map.put("/yecgaa/logout", "logout");
// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
map.put("/yecgaa/**","authc");//表示这个资源需要认证和授权
// 设置认证界面路径
shiroFilterFactoryBean.setLoginUrl("/yecgaa/login");
shiroFilterFactoryBean.setSuccessUrl("/yecgaa/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/404");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}