Shiro原理-过滤器
前言
这几天一直在研究Shiro到底是如何工作的,即一个请求过来了,它是如何做到知道这个请求应该用什么方式来鉴权的?应该调用哪个过滤器?自己定义的过滤器该如何才能生效?
带着这样的疑问,我做了一些测试与研究,并记录于此文。
实现原理
Shiro对于请求的鉴权的实现也是通过过滤器(或者说是拦截器)来实现的,但是Spring项目中有拦截链机制,会有多个拦截器生效,包括系统内置的以及Shiro注入的,所以需要搞懂他的过滤的实现机制就需要去弄明白这些过滤器是如何过滤的。
那就开始吧
ApplicationFilterChain 简介
Tomcat的类ApplicationFilterChain是一个Java Servlet API规范javax.servlet.FilterChain的实现,用于管理某个请求request的一组过滤器Filter的执行。当针对一个request所定义的一组过滤器Filter处理完该请求后,最后一个doFilter()调用才会执行目标Servlet的方法service(),然后响应对象response会按照相反的顺序依次被这些Filter处理,最终到达客户端。
在ApplicationFilterChain的doFilter方法下打上断点
// org.apache.catalina.core.ApplicationFilterChain.java
// 执行过滤器链中的下一个过滤器Filter。如果链中所有过滤器都执行过,则调用servlet的service()方法。
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// 这个if-else分支主要是根据Globals.IS_SECURITY_ENABLED是true还是false决定
// 如何调用目标逻辑,但两种情况下,目标逻辑最终都是 internalDoFilter(req,res)
if (Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Void run() throws ServletException, IOException {
// 调用internalDoFilter
ApplicationFilterChain.this.internalDoFilter(req, res);
return null;
}
});
} catch (PrivilegedActionException var7) {
Exception e = var7.getException();
if (e instanceof ServletException) {
throw (ServletException)e;
}
if (e instanceof IOException) {
throw (IOException)e;
}
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
throw new ServletException(e.getMessage(), e);
}
} else {
// 调用internalDoFilter
this.internalDoFilter(request, response);
}
}
filters
我们可以看到 filters中包含了5个过滤器:
CharacterEncodingFilter:spring内置过滤器,用来指定请求或者响应的编码格式。
FormContentFilter:该过滤器针对DELETE,PUT和PATCH这三种HTTP method分析其FORM表单参数,将其暴露为Servlet请求参数。
RequestContextFilter:该过滤器将当前请求暴露到当前线程。
SpringShiroFilter:shiro内置过滤器,包装 Request 和 Response,使它们由原来的 HttpServlet 系列包装为 ShiroHttpServletRequest等。
Tomcat WebSocket Filter:webSocket 相关过滤器。
这些注入的过滤器会通过internalDoFilter来执行过滤工作,如下:
// org.apache.catalina.core.ApplicationFilterChain.java
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (this.pos < this.n) {
// 如果过滤链中还有过滤器需要过滤
ApplicationFilterConfig filterConfig = this.filters[this.pos++];
try {
// 找到目标的Filter
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
}
// 执行目标 Filter 对象的 doFilter方法,
// 注意,这里当前ApplicationFilterChain对象被传递到了目标
// Filter对象的doFilter方法,而目标Filter对象的doFilter在执行完自己
// 被指定的逻辑之后会反过来调用这个ApplicationFilterChain对象的
// doFilter方法,只是pos向前推进了一个过滤器。这个ApplicationFilterChain
// 和Filter之间反复调用彼此doFilter方法的过程一直持续直到当前链发现所有的
// Filter都已经被执行
if (Globals.IS_SECURITY_ENABLED) {
Principal principal = ((HttpServletRequest)request).getUserPrincipal();
Object[] args = new Object[]{request, response, this};
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (ServletException | RuntimeException | IOException var15) {
throw var15;
} catch (Throwable var16) {
Throwable e = ExceptionUtils