Shiro使用和原理分析—2

为了方便,这里继续引用一下applicationContext.xml中的配置文件

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="filters"><util:map><entry key="authc" value-ref="customAuthenticationFilter"/></util:map></property><property name="filterChainDefinitions"><value>  /test = anon/** = authc</value></property></bean>

假设将web项目部署至tomcat,tomcat会启动socket监听客户端的连接,然后经过层层处理(这里是设计模式中的责任链模式),最后会调用doFilter函数。ShiroFilterFactoryBean经过层层继承,最上层实现了javax.servlet.Filter接口,该接口主要有三个函数init、destroy和doFilter。

上一章讲过ShiroFilterFactoryBean最最终会构造SpringShiroFilter。首先看一下SpringShiroFilter的类结构,

private static final class SpringShiroFilter extends AbstractShiroFilter
public abstract class AbstractShiroFilter extends OncePerRequestFilter
public abstract class OncePerRequestFilter extends NameableFilter
public abstract class NameableFilter extends AbstractFilter implements Nameable
public abstract class AbstractFilter extends ServletContextSupport implements Filter
public class ServletContextSupport

简单来说,AbstractShiroFilter就是处理一些doFilter的逻辑部分,OncePerRequestFilter用来保证shiro对request只过滤一次,NameableFilter和Filter的名字相关,AbstractFilter则是一些基础的配置,ServletContextSupport则是web环境上下文。

SpringShiroFilter的init函数定义在AbstractShiroFilter中,为空函数。
接下来看SpringShiroFilter的doFilter函数,tomcat最后会调用到这个函数,doFilter定义在OncePerRequestFilter中。

doFilter

    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 if (!isEnabled(request, response) || shouldNotFilter(request) ) {log.debug("Filter '{}' is not enabled for the current request.  Proceeding without invoking this filter.",getName());filterChain.doFilter(request, response);} else {log.trace("Filter '{}' not yet executed.  Executing now.", getName());request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);try {doFilterInternal(request, response, filterChain);} finally {request.removeAttribute(alreadyFilteredAttributeName);}}}

该函数通过request中的alreadyFilteredAttributeName属性判断该request是否已经被该filter过滤过,如果已经被过滤了,就跳过该过滤器。另外,过滤器必须设置为enable即开启,如果关闭了该过滤器,isEnabled函数判断enable为false,则也跳过该过滤器。其他情况下,则进入该filter过滤器。首先设置request属性alreadyFilteredAttributeName为true,表是已经通过该过滤器了,下次不经过了,然后调用doFilterInternal。

doFilterInternal定义在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);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;}String msg = "Filtered request failed.";throw new ServletException(msg, t);}}

prepareServletRequest和prepareServletResponse都是对上层传入的参数ServletRequest和ServletResponse进行再包装。
createSubject构造了一个WebSubject,updateSessionLastAccessTime用来更新时间,这里就不往下分析了,后面的章节再来看这些函数。
本章重点分析executeChain这个函数,先看一下

    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)throws IOException, ServletException {FilterChain chain = getExecutionChain(request, response, origChain);chain.doFilter(request, response);}

origChain是上层传来的一个FilterChaingetExecutionChain获得一个新的FilterChain,它和origChain有什么区别呢?往下看。

getExecutionChain

    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;}

首先,getFilterChainResolver获得上一章中构造的PathMatchingFilterChainResolver,所以接下来getChain的函数就定义在PathMatchingFilterChainResolver中,如下所示

    public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {FilterChainManager filterChainManager = getFilterChainManager();if (!filterChainManager.hasChains()) {return null;}String requestURI = getPathWithinApplication(request);for (String pathPattern : filterChainManager.getChainNames()) {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;}

filterChainManager也是上一章在createInstance函数中构造的DefaultFilterChainManager。接下来,从filterChainManager中依次取出chainName(pathPattern)进行比较,如果匹配,就调用proxy函数。filterChainManager中的chainName便是上一章中设置的“/test”和“/**”。

proxy

    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);}

首先看一下getChain函数,

    public NamedFilterList getChain(String chainName) {return this.filterChains.get(chainName);}

上一章中,在filterChains中根据过滤器链名chainName设置的NamedFilterList为SimpleNamedFilterList,因此继续看SimpleNamedFilterList的proxy函数。

    public FilterChain proxy(FilterChain orig) {return new ProxiedFilterChain(orig, this);}

好了,到这里,回头,层层往上再到前面的executeChain函数,开始调用doFilter函数。总结一下,getExecutionChain返回的其实是一个ProxiedFilterChain实例,这个实例是根据request中取得本次客户端请求的相对路径再和chainName匹配得到的。因此,看一下ProxiedFilterChain的doFilter函数。

doFilter

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {if (this.filters == null || this.filters.size() == this.index) {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);}}

简单来说,这里就是在原本的过滤器中插入shiro的过滤器。shiro的过滤器有啥,便是配置的anon和authc对应的过滤器,一个是默认的过滤器AnonymousFilter(上一章),另一个则是自定义的customAuthenticationFilter。下一章继续介绍它们。

Shiro使用和原理分析---2相关推荐

  1. Shiro(三) Shiro核心原理分析

    一.Shiro登陆认证原理 对于登陆请求(URL=/login.html),我们一般使用"anno"拦截器去过滤处理,而这个filter又是放行所有资源的(不作任何处理),所以登陆 ...

  2. Shiro框架原理及应用分析

    shiro 介绍 ounter(line Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权.加密.会话管理等功能,组成了一个通用的安全认证 ...

  3. 安全认证框架Shiro (二)- shiro过滤器工作原理

    安全认证框架Shiro (二)- shiro过滤器工作原理 安全认证框架Shiro 二- shiro过滤器工作原理 第一前言 第二ShiroFilterFactoryBean入口 第三请求到来解析过程 ...

  4. java signature 性能_Java常见bean mapper的性能及原理分析

    背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换.赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils.BeanCopier.Dozer. ...

  5. Select函数实现原理分析

    转载自 http://blog.chinaunix.net/uid-20643761-id-1594860.html select需要驱动程序的支持,驱动程序实现fops内的poll函数.select ...

  6. spring ioc原理分析

    spring ioc原理分析 spring ioc 的概念 简单工厂方法 spirng ioc实现原理 spring ioc的概念 ioc: 控制反转 将对象的创建由spring管理.比如,我们以前用 ...

  7. 一次 SQL 查询优化原理分析(900W+ 数据,从 17s 到 300ms)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:Muscleape jianshu.com/p/0768eb ...

  8. 原理分析_变色近视眼镜原理分析

    随着眼镜的发展,眼镜的外型变得越来越好看,并且眼镜的颜色也变得多姿多彩,让佩戴眼镜的你变得越来越时尚.变色近视眼镜就是由此产生的新型眼镜.变色镜可以随着阳光的强弱变换不同的色彩. 变色眼镜的原理分析 ...

  9. jieba分词_从语言模型原理分析如何jieba更细粒度的分词

    jieba分词是作中文分词常用的一种工具,之前也记录过源码及原理学习.但有的时候发现分词的结果并不是自己最想要的.比如分词"重庆邮电大学",使用精确模式+HMM分词结果是[&quo ...

最新文章

  1. 如何用计算机声卡,外置声卡怎么连接电脑
  2. [转]解决安装m2eclipse插件后,eclipse启动时在控制台提示的警告
  3. 如何在网页中禁止使用鼠标右键?几种方法和大家分享
  4. ionic3使用@angular/http 访问nodejs(koa2框架)服务不能返回数据
  5. ios学习--TableView详细解释
  6. 解决Centos 7 VNC黑屏
  7. java 设置两个方法互斥_分享两个操作Java枚举的实用方法
  8. 主键、聚集索引、非聚集索引区别
  9. 微软在 Build 2020 上“展示”新版 Edge for Linux
  10. JavaScript相关技术学习
  11. miRNA数据库篇——HMDD:miRNA相关疾病数据库
  12. 微信公众号支付失败的各种原因
  13. 第二届上汽零束SOA平台开发者大会揭幕,智能汽车生态加速落地
  14. php程序员开发工具箱,php程序员工具箱|php工具箱 v0.7 官方版 - 软件下载 - 绿茶软件园|33LC.com...
  15. java 错误 找不到符号_java错误:找不到符号
  16. 计算机辅助翻译与人工智能,2018年机器翻译行业概述与现状,人工智能让人人实现国际化交流...
  17. Android下调用收发短信邮件等
  18. 第一次软工作业(数独)
  19. 一文了解各大图数据库查询语言(Gremlin vs Cypher vs nGQL)| 操作入门篇
  20. 免疫表位数据库(IEDB)数据下载操作实例

热门文章

  1. (2021年)is not a supported wheel on this platform解决方案
  2. 单链表的基本操作----------头插法/尾插法建立链表
  3. Java斐波那契数列
  4. Csharp零基础:第一天学Csharp要会的基础知识
  5. Csharp日常笔记
  6. 网站应用开发Web App Development:如何让前端与后端快速地跑起来
  7. 目前计算机常用的硬盘类型,干货大放送,电脑硬盘分类你知道几个?
  8. 电脑开机密码忘记,如何修改电脑密码?
  9. IOException parsing XML document from class path resource [applicationContent.xml]; nested exception
  10. 最新版树莓派3A+ 开箱简介