shiro: 1.6
spring: 5.2
访问:http://localhost:8080/demo/;JSESSIONID=655def62-75b3-4ab1-ae27-b7d0e42c431a时, 出现400错误页面,无法跳转到登录页。
当浏览器本地cookie禁用或者部分链接跳转时,会默认加上 ;JSESSIONID= 参数传递cookie中存储的sessionId, 后端Filter过滤器会首先尝试从cookie中获取sessionId,获取不到时尝试解析uri连接(;/JSESSIONID=xxx)获取sessionId,再获取不到尝试从请求参数中获取。
正常情况下,访问该链接,若session过期,会自动跳转到登录页。
但是再shiro:1.6开始,新增了一个InvalidRequestFilter的过滤器,用于拦截存在安全问题的uri,该过滤器拦截三种类型的uri并返回400拒绝访问:(http://xxx/;JSESSIONID=xxx中存在分号)
public class InvalidRequestFilter 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"));
// 拦截uri地址中存在分号的请求
private boolean blockSemicolon = true;
// 拦截uri中存在反斜杠的请求
private boolean blockBackslash = true;
// 拦截uri中存在非ascii码的请求
private boolean blockNonAscii = true;
....省略....
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
String uri = WebUtils.toHttp(request).getRequestURI();
return !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;
}
}
对于所有进来的请求,过滤器都会进行一次前置和后置判断,确认是否继续执行剩余未执行的过滤器。
AdviceFilter:
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
Exception exception = null;
try {
// 当请求uri被认为不合法是返回false
boolean continueChain = preHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]");
}
if (continueChain) {
executeChain(request, response, chain);
}
postHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Successfully invoked postHandle method");
}
} catch (Exception e) {
exception = e;
} finally {
cleanup(request, response, exception);
}
}
preHandle方法最终实现再AccessControlFilter中:
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// 这两个方法就是InvalidRequestFilter中的方法,InvalidRequestFilter是AccessControlFilter的子类
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}
处理方法主要有三种:
1. xml配置覆盖默认的InvalidRequestFilter: blockSemicolon设置为false
<bean id="invalidRequestFilter" class="org.apache.shiro.web.filter.InvalidRequestFilter">
<property name="blockSemicolon" value="false" />
</bean>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
....省略...
<property name="filters">
<map>
名字key要与默认(invalidRequest)的相同才能覆盖初始化时的InvalidRequestFilter
<entry key="invalidRequest" value-ref="invalidRequestFilter" />
</map>
</property>
</bean>
2. shiro降到1.6之前(或许1.6之后有改进,暂时没有关注)
3. 自定义ShiroFilter并设置setEnable(false) 将不在进行上述三种类型uri安全性拦截