shiro-web
看之前可以先了解:shiro-core 分析
shiro-web是shiro-core的wrapper:
其特征都是 ”实现一方,调用一方“ 注:调用不一定要显式的调用(代码上的),隐式调用也算,比如子类对于没有重写父类的方法,其字节码的函数引用与父类的一致,所以也可算作子类调用了父类的函数
比如shiro-web实现了Filter接口,从而使得Tomcat可以调用,同时ShiroFilter,又调用了WebSubject,
从抽象的接口的意义上讲,Shiro***Filter调用WebSubject相关验证和授权接口,但这些接口是继承自Subject的,所以会调用Subject接口,从而调用了shiro-core
如图:红色代表shiro-core, 蓝色是shiro-web
这里的ShiroFilter不是具体的某个类,而是代表所有shiro内的Filter
从具体实现的意义过程来讲,当然是类调用类,但这是实现,不是"概念",从抽象意义讨论调用过程比较契合概念.
同时由于其是软件级别的wrapper,shiro-core与其wrapper(shiro-web) 不是完全单向的调用关系,某些情况shiro-core会调用wrapper中的实现,比如SessionManager的两种实现
同时shiro-web与Servlet容器也不是单向关系
同时从上图可以看出其符合架构(Architecture)中的分层结构(Layered)
session管理冲突:
Session管理的使用,servlet容器(Tomcat) 已经实现了 的(Tomcat的Session管理(一) - coldridgeValley - 博客园 (cnblogs.com)),所以到底是使用 shrio提供的SessionManager,还是Tomcat已经实现的 是一个需要考虑的选择,shiro当然考虑到了这点
subject 不在是“用户”级别 更确切的讲是“请求”级别,因为Servlet Container是以请求为单位进行处理,所以对它来说“主体” 是 请求
结构分析 && 流程分析
shiro-web主要通过继承的方式,让shiro-web容器直接依赖Webxxx
自己的类,从而实现shiro-web与shiro-core解耦
eg:
类似的还有WebSecurityManger,
servlet容器直接依赖shiroFilter,shiroFilter又直接依赖WebSubject,WebSubject又直接依赖WebSecurityManger....它们的逻辑大部分都由shiro-core实现,这种方式使得shiro-web不在代码上直接依赖shiro-core:shiro-web与shiro-core解耦
因此对于这些部分不再赘述,直接看shiro-core即可,只分析shiro-web特有部分;
==由于shiro-web特有部分非常少故直接从实现阶段分析==
Filter
(Architecture,Implement)
从OncePerRequestFilter开始有一个分支,其中右边是直接对接servlet容器中的Filter,也就是说对于servlet容器,shiro-web只是一个Filter,其名字为ShiroFilter,(通过web.xml配置)
左边是shiro-web实际执行过滤逻辑的Filter
OncePerRequestFilter
OncePerRequestFilter.doFilter()
实际逻辑由doFilterInternal()决定,它由子类实现
ShiroFilter(AbstractShiroFilter)
初始化(读取解析配置文件)
doFilter()
AbstractShiroFilter::
getExecutionChain()返回的chain是ProxiedFilterChain,后面讨论
AdviceFilter
A Servlet Filter that enables AOP-style "around" advice for a ServletRequest via preHandle, postHandle, and afterCompletion hooks.
preHandle等“advice()"主要由子类实现;
AccessControlFilter
主要分类:最重要的是左边两个分类
InvalidRequestFilter:用于封锁恶意请求
UserFilter:
Filter that allows access to resources if the accessor is a known user, which is defined as having a known principal. This means that any user who is authenticated or remembered via a 'remember me' feature will be allowed access from this filter. If the accessor is not a known user, then they will be redirected to theloginUrl
AuthenticationFilter
AuthorizationFilter
FilterChainManager
A FilterChainManager manages the creation and modificationof Filter chains from an available pool of Filter instances.
NamedFilterList extend List
NamedFilterList是装载NameableFilter(及其子类)的列表
filters与filterChains是最核心的数据,这些字段在初始化阶段,通过读取解析配置文件获取
eg: shiro.ini
[main]
...
自定义filter也可以被解析到filters pool
myFilter = com.company.web.some.FilterImplementation
myFilter.property1 = value1
otherFilter = .....
...
[urls]
...
key是url同时也是filterChain的名字,value部分是filtername,可以配置多个filtername(必须在main部分声明,除非使用的是# Default Filter的默认名字)
/some/path/** = myFilter,otherFilter
以下是Default Filter及其name ![image](https://image.3001.net/images/20241030/1730281738_6722010adc471f3948b95.png!small)from:[Apache Shiro Web Support\#default\_filters \| Apache Shiro](https://shiro.apache.org/web.html#default_filters)
FilterChainResolver
根据request.url 返回对应的FilterChain
其实现类只有一个:PathMatchingFilterChainResolver
实现方式很简单,获取requestUrl, 再遍历所有filterChainNames,并一个一个比对,比对成功则调用FIlterChainManger.proxy(),并返回,也就起到一个匹配filterchainNames的作用
跟进proxy():
//DefaultFilterChainManager::
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); //委托
}
//SimpleNamedFilterList::
public FilterChain proxy(FilterChain orig) {
return new ProxiedFilterChain(orig, this);
}
ProxiedFilterChain
architecture
filters: shiro实际的过滤器
orig: servlet容器中原来的链
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 (LOGGER.isTraceEnabled()) {
LOGGER.trace("Invoking original filter chain.");
}
this.orig.doFilter(request, response);
} else {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Invoking wrapped filter at index [" + this.index + "]");
}
this.filters.get(this.index++).doFilter(request, response, this);
}
}
}
大致逻辑就是先调用filters的doFilter,当执行完后,就执行orig.doFilter(...) ,这也意味着ShiroFilter结束了
总体流程
根据上述内容得出:(不包括初始化部分)