filter执行的是代理bean的id为shiroFilter相应的方法
接下来看看 shiroFilter 是什么
再来分析shiroFilter 这个实现了 javax.servlet.Filter 的生命周期
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 --> <property name="loginUrl" value="/login.action" /> <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 --> <property name="successUrl" value="/index.action" /> <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面 --> <property name="unauthorizedUrl" value="/refuse.action" /> <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 --> <property name="filterChainDefinitions"> <value> <!-- 静态资源放行 --> /login/account = anon /user = perms["user:view"] <!--商品查询需要商品查询权限 ,取消url拦截配置,使用注解授权方式 --> <!-- /itemEdit.action = perms[item:edit] --> <!-- 请求 logout.action地址,shiro去清除session --> /logout = logout <!-- /** = authc 所有url都必须认证通过才可以访问 --> /** = authc <!-- 所有url都可以匿名访问 --> <!-- /** = anon --> </value> </property> </bean> <!-- securityManager安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 注入realm --> <property name="realm" ref="customRealm" /> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- 会话管理器 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- session的失效时长,单位毫秒 --> <property name="globalSessionTimeout" value="600000" /> <!-- 删除失效的session --> <property name="deleteInvalidSessions" value="true" /> </bean> <!-- realm --> <bean id="customRealm" class="org.apache.shiro.realm.text.IniRealm"> <constructor-arg index="0" value="classpath:shiro.ini"/> </bean>

ShiroFilterFactoryBean 实现了 FactoryBean ,那么实际上getBean的时候,返回的是getObject的内容而不是ShiroFilterFactoryBean
(如果不理解实现 FactoryBean 会返回 getObject 的同学, 期待接下来的spring源码分析,或者自行百度)
接下来分析 ShiroFilterFactoryBean 的getObject 返回的对象是什么,才能知道 init,doFilter,destory 的时候做了什么
ShiroFilterFactoryBean
public Object getObject() throws Exception { if (instance == null) { instance = createInstance(); } return instance; }
级别2
instance = createInstance();
- 验证securityManager不为空,验证 securityManager 必须是 WebSecurityManager
- 创建 过滤链管理器(责任链模式),将shiro默认的验证类型和用户自己定义的过滤器添加到管理器
- 返回一个 SpringShiroFilter 对象
ShiroFilterFactoryBean
protected AbstractShiroFilter createInstance() throws Exception { log.debug("Creating Shiro Filter instance."); 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); //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 SpringShiroFilter((WebSecurityManager) securityManager, chainResolver); }
所以在web中配置的 shiroFilter 实际上是一个 SpringShiroFilter 对象,我们要分析的shiro在web中的使用,也就是分析 SpringShiroFilter 的生命周期
shiroFilter

init
- 设置filterConfig和servletContext
- 确保 this.securityManager 已被初始化
SpringShiroFilter
public void setFilterConfig(FilterConfig filterConfig) { this.filterConfig = filterConfig; setServletContext(filterConfig.getServletContext()); } public final void init(FilterConfig filterConfig) throws ServletException { setFilterConfig(filterConfig); try { onFilterConfigSet(); } catch (Exception e) { if (e instanceof ServletException) { throw (ServletException) e; } else { if (log.isErrorEnabled()) { log.error("Unable to start Filter: [" + e.getMessage() + "].", e); } throw new ServletException(e); } } }
级别2
onFilterConfigSet();
- 当 init-param 的 staticSecurityManagerEnabled 不为空就设置 this.staticSecurityManagerEnabled
- init是空的,留给子类去实现,当前情况下没有子类去实现,就是空的
- 当 securityManager 为空的时候,创建默认的securityManager
- 判断 this.staticSecurityManagerEnabled 为true,就设置 SecurityUtils.securityManager
AbstractShiroFilter
// 当 init-param 的 staticSecurityManagerEnabled 不为空就设置 this.staticSecurityManagerEnabled private void applyStaticSecurityManagerEnabledConfig() { String value = getInitParam(STATIC_INIT_PARAM_NAME); if (value != null) { Boolean b = Boolean.valueOf(value); if (b != null) { setStaticSecurityManagerEnabled(b); } } } //当 securityManager 为空的时候,创建默认的securityManager private void ensureSecurityManager() { WebSecurityManager securityManager = getSecurityManager(); if (securityManager == null) { log.info("No SecurityManager configured. Creating default."); securityManager = createDefaultSecurityManager(); setSecurityManager(securityManager); } } protected final void onFilterConfigSet() throws Exception { //added in 1.2 for SHIRO-287: applyStaticSecurityManagerEnabledConfig(); init(); ensureSecurityManager(); //added in 1.2 for SHIRO-287: if (isStaticSecurityManagerEnabled()) { SecurityUtils.setSecurityManager(getSecurityManager()); } }
doFilter
父类 OncePerRequestFilter 实现了 doFilter
- 判断是否有 filterName().FILTERED,不为空就 执行下一个过滤器
- 判断 没有启用过滤器 或者 request 没有过滤器就执行下一个过滤器
- 设置 filterName().FILTERED = true, 执行当前过滤器
OncePerRequestFilter
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); if ( request.getAttribute(alreadyFilteredAttributeName) != null ) { log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName()); filterChain.doFilter(request, response); } else //noinspection deprecation if (/* added in 1.2: */ !isEnabled(request, response) || /* retain backwards compatibility: */ shouldNotFilter(request) ) { log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.", getName()); filterChain.doFilter(request, response); } else { // Do invoke this filter... log.trace("Filter '{}' not yet executed. Executing now.", getName()); request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { doFilterInternal(request, response, filterChain); } finally { // Once the request has finished, we're done and we don't // need to mark as 'already filtered' any more. request.removeAttribute(alreadyFilteredAttributeName); } } }
级别2
doFilterInternal(request, response, filterChain);
- 用 ShiroHttpServletRequest 包装servletRequest,ShiroHttpServletResponse 包装servletResponse (这是一个典型的装饰模式)
- 创建当前用户 Subject
- 当前用户执行访问目标操作
在这里创建用户的部分可以参考shiro 基础章节, 就是从 this.securityManager.createSubject(this.subjectContext);
访问目标 subject.execute 就是接下来进行重点讲解的了
AbstractShiroFilter
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain) throws ServletException, IOException { Throwable t = null; try { final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain); final ServletResponse response = prepareServletResponse(request, servletResponse, chain); final Subject subject = createSubject(request, response); //noinspection unchecked subject.execute(new Callable() { public Object call() throws Exception { updateSessionLastAccessTime(request, response); executeChain(request, response, chain); return null; } }); } catch (ExecutionException ex) { t = ex.getCause(); } catch (Throwable throwable) { t = throwable; } if (t != null) { if (t instanceof ServletException) { throw (ServletException) t; } if (t instanceof IOException) { throw (IOException) t; } //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one: String msg = "Filtered request failed."; throw new ServletException(msg, t); } }
级别3-1
subject.execute
这里就是一个很典型的代理模式 ,用 SubjectCallable 代理了原来的 callable.
然后再执行真正的call
public SubjectCallable(Subject subject, Callable<V> delegate) { // 设置TheadState this(new SubjectThreadState(subject), delegate); } public <V> Callable<V> associateWith(Callable<V> callable) { //返回代理类 return new SubjectCallable<V>(this, callable); } public <V> V execute(Callable<V> callable) throws ExecutionException { Callable<V> associated = associateWith(callable); try { // 代理类 call return associated.call(); } catch (Throwable t) { throw new ExecutionException(t); } }
级别3-2
updateSessionLastAccessTime(request, response);
通过名字就可以判断出来, 更新session的最后访问时间。
但是访问的时候带sessionId会报异常,不过没什么影响,可以忽略,或者关闭这个日志
http://localhost:8080/login.action;JSESSIONID=2bf853ec-f439-4981-8c01-7bfa7456c878
org.apache.shiro.session.UnknownSessionException: There is no session with id [b81ed452-882c-4cd1-a8e8-b1bfd7e3cee8] at org.apache.shiro.session.mgt.eis.AbstractSessionDAO.readSession(AbstractSessionDAO.java:170)
级别3-3
executeChain(request, response, chain);
这段内容也比较简单, 就是用当前的 FilterChainResolver 去获取 FilterChain ,
然后返回去执行 chain.doFilter(request, response);
这一段的重点内容也就在于 resolver.getChain(request, response, origChain);
这里会具体进行角色权限的判断,判断用户是不是有url的访问权限
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain) throws IOException, ServletException { FilterChain chain = getExecutionChain(request, response, origChain); chain.doFilter(request, response); }
级别1
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) { FilterChain chain = origChain; FilterChainResolver resolver = getFilterChainResolver(); if (resolver == null) { log.debug("No FilterChainResolver configured. Returning original FilterChain."); return origChain; } FilterChain resolved = resolver.getChain(request, response, origChain); if (resolved != null) { log.trace("Resolved a configured FilterChain for the current request."); chain = resolved; } else { log.trace("No FilterChain configured for the current request. Using the default."); } return chain; }
级别2
FilterChain resolved = resolver.getChain(request, response, origChain);
获得当前链的操作 做了一下几个步骤
- 获取 FilterChainManager (在ShiroFilterFactoryBean的getObject下面的createInstance创建的)
- 获取 requestURI
- 遍历配置的 filterChainDefinitions 属性的key。 判断key是否能匹配requestURI。 如果能匹配 就返回value对应的Filter
接下来解析如何通过value获取对应的filter
如果有更多的兴趣: 可以去查看
PathMatchingFilterChainResolver
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) { FilterChainManager filterChainManager = getFilterChainManager(); if (!filterChainManager.hasChains()) { return null; } String requestURI = getPathWithinApplication(request); //the 'chain names' in this implementation are actually path patterns defined by the user. We just use them //as the chain name for the FilterChainManager's requirements for (String pathPattern : filterChainManager.getChainNames()) { // If the path does match, then pass on to the subclass implementation for specific checks: if (pathMatches(pathPattern, requestURI)) { if (log.isTraceEnabled()) { log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "]. " + "Utilizing corresponding filter chain..."); } return filterChainManager.proxy(originalChain, pathPattern); } } return null; }
级别3
return filterChainManager.proxy(originalChain, pathPattern);
这里就是通过 配置的key获取对应的Filter
例如: /* = authc 就是通过/* 获取: NamedFilterList
DefaultFilterChainManager
public NamedFilterList getChain(String chainName) { return this.filterChains.get(chainName); } public FilterChain proxy(FilterChain original, String chainName) { NamedFilterList configured = getChain(chainName); if (configured == null) { String msg = "There is no configured chain under the name/key [" + chainName + "]."; throw new IllegalArgumentException(msg); } return configured.proxy(original); }
级别4
return configured.proxy(original);
这里可以看到,是通过 NamedFilterList 和 FilterChain 创建了一个代理对象 ProxiedFilterChain
SimpleNamedFilterList
public FilterChain proxy(FilterChain orig) { return new ProxiedFilterChain(orig, this); }
chain.doFilter(request, response);
通过 就已经获取到了 FilterChain 对象。
返回的是一个 ProxiedFilterChain 对象。
这一步实际上跟进的就是 ProxiedFilterChain.doFilter
到这里为止,其实就已经分析完成了。因为类似: /** = authc 可以有各种各样的配置。
在最下面的附注会附上 perms 和 authc 的doFilter 分析
ProxiedFilterChain
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (this.filters == null || this.filters.size() == this.index) { //we've reached the end of the wrapped chain, so invoke the original one: if (log.isTraceEnabled()) { log.trace("Invoking original filter chain."); } this.orig.doFilter(request, response); } else { if (log.isTraceEnabled()) { log.trace("Invoking wrapped filter at index [" + this.index + "]"); } this.filters.get(this.index++).doFilter(request, response, this); } }
destory
卸载的时候什么都不做
AbstractFilter
public void destroy() { }
做的事情非常简单,就是在执行目标前后进行 threadState 的设置和还原
其实就是设置当前线程的 Subject 和 SecurityManager
SubjectCallable
protected V doCall(Callable<V> target) throws Exception { return target.call(); } public V call() throws Exception { try { threadState.bind(); return doCall(this.callable); } finally { threadState.restore(); } }
shiro内置权限认证对象
authc对应的filter: FormAuthenticationFilter

FormAuthenticationFilter 继承 OncePerRequestFilter 那么 doFilter 实际上就是执行 doFilterInternal
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { //TODO 省略 doFilterInternal(httpRequest, httpResponse, filterChain); }
级别2
doFilterInternal(httpRequest, httpResponse, filterChain);
这里的代码整体流程也非常清晰
1. 预处理请求 (进行url匹配,匹配成功后进行认证。 认证成功后 continueChain=true)
2. 通过判断是否要执行调用链,
3. 清理调用链 (在这里什么都没做)
那么这里就可以确定最最重要的部分 perHandler
AdviceFilter
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { Exception exception = null; try { 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); } }
级别3
boolean continueChain = preHandle(request, response);
- 这里判断是否存在当前匹配器 ,例如 全局都没有配置 /user = authc, 那么就表示没有authc匹配器
- 遍历匹配器的key,用请求的uri和配置的匹配器进行匹配
- 进入下一阶段获取是否可以继续执行filterChain
PathMatchingFilter
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { if (this.appliedPaths == null || this.appliedPaths.isEmpty()) { if (log.isTraceEnabled()) { log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately."); } return true; } for (String path : this.appliedPaths.keySet()) { // If the path does match, then pass on to the subclass implementation for specific checks //(first match 'wins'): if (pathsMatch(path, request)) { log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path); Object config = this.appliedPaths.get(path); return isFilterChainContinued(request, response, path, config); } } //no path matched, allow the request to go through: return true; }
级别4
return isFilterChainContinued(request, response, path, config);
onPreHandle 由 实现.
PathMatchingFilter
private boolean isFilterChainContinued(ServletRequest request, ServletResponse response, String path, Object pathConfig) throws Exception { if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2 if (log.isTraceEnabled()) { log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}]. " + "Delegating to subclass implementation for 'onPreHandle' check.", new Object[]{getName(), path, pathConfig}); } //The filter is enabled for this specific request, so delegate to subclass implementations //so they can decide if the request should continue through the chain or not: return onPreHandle(request, response, pathConfig); } if (log.isTraceEnabled()) { log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}]. " + "The next element in the FilterChain will be called immediately.", new Object[]{getName(), path, pathConfig}); } //This filter is disabled for this specific request, //return 'true' immediately to indicate that the filter will not process the request //and let the request/response to continue through the filter chain: return true; }

- 判断是否允许访问, 允许访问就返回true
- 拒绝访问(无论如何都返回false)
接下来就要进行两部分分析了 isAccessAllowed 和 onAccessDenied
AccessControlFilter
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue); }
isAccessAllowed
- 判断subject 是否已经认证
- 判断是不是loginUrl
- 判断是否有授权
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { return super.isAccessAllowed(request, response, mappedValue) || (!isLoginRequest(request, response) && isPermissive(mappedValue)); } //判断subject 是否已经认证 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { Subject subject = getSubject(request, response); return subject.isAuthenticated(); } //判断是不是loginUrl protected boolean isLoginRequest(ServletRequest request, ServletResponse response) { return pathsMatch(getLoginUrl(), request); } protected boolean isPermissive(Object mappedValue) { if(mappedValue != null) { String[] values = (String[]) mappedValue; return Arrays.binarySearch(values, PERMISSIVE) >= 0; } return false; } // 是否有访问权限, protected boolean isPermissive(Object mappedValue) { if(mappedValue != null) { String[] values = (String[]) mappedValue; return Arrays.binarySearch(values, PERMISSIVE) >= 0; } return false; }
onAccessDenied
调用子类实现,发现子类无论如何都会返回false
也就是说 当isAccessAllowed的结果为false(不允许访问的时候)
1. 当前用户的凭证信息为空的时候, 跳转到登录页
2. 判断是否已经配置 unauthorizedUrl (未授权跳转到的url),如果已经配置就跳转到 unauthorizedUrl, 如果没有配置没权限页面,返回错误码401 (HttpServletResponse.SC_UNAUTHORIZED)
AccessControlFilter
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return onAccessDenied(request, response); }
AuthorizationFilter
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { Subject subject = getSubject(request, response); // If the subject isn't identified, redirect to login URL if (subject.getPrincipal() == null) { saveRequestAndRedirectToLogin(request, response); } else { // If subject is known but not authorized, redirect to the unauthorized URL if there is one // If no unauthorized URL is specified, just return an unauthorized HTTP status code String unauthorizedUrl = getUnauthorizedUrl(); //SHIRO-142 - ensure that redirect _or_ error code occurs - both cannot happen due to response commit: if (StringUtils.hasText(unauthorizedUrl)) { WebUtils.issueRedirect(request, response, unauthorizedUrl); } else { WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED); } } return false; }