Spring-shiro源码陶冶-DelegatingFilterProxy和ShiroFilterFactoryBean
阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用
介绍
Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理主要功能;另外其也提供了Web Support、缓存、Remember Me、并发等功能。
而Shiro的架构核心可看来自Java技术栈提供的图片

对上述的图作简单的描述
- Subject 用户视图,shiro对用户或者第三方服务、任何需要登录的东西统一称之为Subject对象
- SecurityManager 安全管理器,其中内含缓存管理、会话管理,主要是对登录过来的Subject视图作一系列的安全操作(包括下述提及的Realms的关联使用)
- Realms 数据管理器,其主要充当shiro与安全数据交互的桥梁,帮助shiro容器完成用户的校验以及认证功能。可配置多个,但必须配置至少一个。shiro也提供了默认的实现比如ladp/jdbc等方式的用户校验认证
更多的部分读者可参阅知乎Java技术栈专栏文章非常详尽的 Shiro 架构解析。本文只分析其与Spring如何搭配使用
web.xml配置Shiro环境
配置清单如下
<!-- shiro 安全过滤器 -->
<filter>
<filter-name>shiroFilter</filter-name>
<!--spring调用shiro代理-->
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<init-param>
<!--是否开启Filter的生命周期,主要涉及init和destory-->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
DelegatingFilterProxy#initFilterBean
入口方法,首先会执行初始化工作,shiro和spring security的代理加载实体Filter类都是通过此入口方法,代码清单如下
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.如果没有指定则采用对应的<filter-name>的值
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
//初始化
this.delegate = initDelegate(wac);
}
}
}
}
我们主要关心DelegatingFilterProxy#initDelegate初始化委托Filter方法,代码清单如下
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
//根据名字获取ApplicationContext环境的bean,且必须是Filter的实现类
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
//true则初始化对应的Filter类
if (isTargetFilterLifecycle()) {
//这里一般对应的Filter类为`org.apache.shiro.spring.web.ShiroFilterFactoryBean`
delegate.init(getFilterConfig());
}
return delegate;
}
由以上代码可以确认,application-shiro.xmlSpring配置文件必须含有与web.xml中DelegatingFilterProxy类对应的<filter-name>的bean配置
示例文件
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--securityManager属性必须存在,通过安全管理器调用Realm接口实现的认证/授权方法-->
<property name="securityManager" ref="securityManager"/>
<!--登录、主页、未通过授权页面的url-->
<property name="loginUrl" value="/login"/>
<property name="successUrl" value="/index.html"/>
<property name="unauthorizedUrl" value="/403.html"/>
<!--自定义的filter集合-->
<property name="filters">
<!--这里采用map集合主要是响应内部属性Map<String, Filter> filters-->
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="perm" value-ref="permissionsAuthorizationFilter"/>
<entry key="sysUser" value-ref="sysUserFilter"/>
<entry key="user" value-ref="userFilter"/>
</util:map>
</property>
<!--对应路径请求Filter链,=右边代表是filter过滤引用,且是顺序执行,url且从上置下优先匹配,一旦匹配则不往下搜寻-->
<!--=右边表达也可为authc[role1,role2]表明访问该url必须通过认证且具有role1、role2的角色权限-->
<property name="filterChainDefinitions">
<value>
/test/** = anon
/login = captcha,authc
/index = anon
/403.html = anon
/login.html = anon
/favicon.ico = anon
/static/** = anon
/index.html=user,sysUser
/welcome.html=user,sysUser
/** = user,sysUser,perm
</value>
</property>
</bean>
ShiroFilterFactoryBean#createInstance方法返回Filter实例
通过ShiroFilterFactoryBean#createInstance方法创建对应的Filter实例,我们看下创建的实例是何种人物,代码清单如下
//创建实例,实例对象为SpringShiroFilter
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
//<property name="securityManager">属性必须存在
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
//securityManager的实现类必须是WebSecurityManager的实现类,表明Spring的shiro结合是web方面的
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
//filter过滤链管理类创建
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);
}
其中涉及filter过滤链管理类的创建,简单分析下,代码清单如下
protected FilterChainManager createFilterChainManager() {
//采用默认filter过滤链管理类
DefaultFilterChainManager manager = new DefaultFilterChainManager();
//获取默认Filter类集合
Map<String, Filter> defaultFilters = manager.getFilters();
//apply global settings if necessary:
for (Filter filter : defaultFilters.values()) {
//主要设置loginUrl、successUrl、unauthorizedUrl
//即权限Filter类设置loginUrl;认证Filter类设置successUrl、loginUrl;授权Filter类设置unauthorizedUrl,loginUrl
applyGlobalPropertiesIfNecessary(filter);
}
//Apply the acquired and/or configured filters:用户自定义的Filter类,通过<property name="filters">设定的
Map<String, Filter> filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry<String, Filter> entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
//同样设置url属性
applyGlobalPropertiesIfNecessary(filter);
//设置名字
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
//'init' argument is false, since Spring-configured filters should be initialized
//in Spring (i.e. 'init-method=blah') or implement InitializingBean:
manager.addFilter(name, filter, false);
}
}
//build up the chains: url与对应的filter过滤链配置,解析的是<property name="filterChainDefinitions">属性
//url可对应多个filter,且保存filter是通过ArrayList来保存的,表明过滤链则由写的先后顺序执行
Map<String, String> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
//chainDefinition可能为authc[role1,role2]或者authc,anno等
String chainDefinition = entry.getValue();
//解析以上表达式并保存至相应的自定义filter对象中
manager.createChain(url, chainDefinition);
}
}
return manager;
}
总结
Spring容器中使用Apache Shiro,需要配置
- web.xml中配置节点,节点类为org.springframework.web.filter.DelegatingFilterProxy
- spring配置shiro文件中需要节点,节点id名必须与web.xml定义中的节点的属性一致,且beanClass为
org.apache.shiro.spring.web.ShiroFilterFactoryBeanSpring配置文件中配置
org.apache.shiro.spring.web.ShiroFilterFactoryBean主要设置shiro的相关filter用于拦截请求,其中的属性含义见本文的示例文件说明
下节预告
Spring-shiro源码陶冶-DefaultFilter
Spring-shiro源码陶冶-DelegatingFilterProxy和ShiroFilterFactoryBean的更多相关文章
- Spring-shiro源码陶冶-DefaultFilter
阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用 简单介绍 Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能 Apache Shiro自带的 ...
- shiro源码篇 - shiro的filter,你值得拥有
前言 开心一刻 已经报废了一年多的电脑,今天特么突然开机了,吓老子一跳,只见电脑管家缓缓地出来了,本次开机一共用时一年零六个月,打败了全国0%的电脑,电脑管家已经对您的电脑失去信心,然后它把自己卸载了 ...
- Shiro 源码分析
http://my.oschina.net/huangyong/blog/215153 Shiro 是一个非常优秀的开源项目,源码非常值得学习与研究. 我想尝试做一次 不一样 的源码分析:源码分析不再 ...
- Spring mybatis源码篇章-MybatisDAO文件解析(二)
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...
- Spring mybatis源码篇章-MybatisDAO文件解析(一)
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...
- Spring mybatis源码篇章-NodeHandler实现类具体解析保存Dynamic sql节点信息
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource SqlNode接口类 publi ...
- Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(二) 首先了解下sql mapper的动态sql语法 具体的动态sql的 ...
- Spring mybatis源码学习指引目录
前言: 分析了很多方面的mybatis的源码以及与spring结合的源码,但是难免出现错综的现象,为了使源码陶冶更为有序化.清晰化,特作此随笔归纳下分析过的内容.博主也为mybatis官方提供过pul ...
- Springboot security cas源码陶冶-ExceptionTranslationFilter
拦截关键的两个异常,对异常进行处理.主要应用异常则跳转至cas服务端登录页面 ExceptionTranslationFilter#doFilter-逻辑入口 具体操作逻辑如下 public void ...
随机推荐
- hbmy周赛1--C
C - Exam Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Submit St ...
- c语言sizeof用法(32位机)
- Spring框架学习笔记(10)——Spring中的事务管理
什么是事务 举例:A给B转500,两个动作,A的账户少500,B的账户多500 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 一.注解添加事务管理方 ...
- p2p项目,自己期望太高了。
在项目的进行中,主要牵扯到了一些安全方面的考虑,跟money相关的嘛,如果安全不考虑,你就惨了,一期呢,为了防止数据被篡改,主要对数据用dsa进行了签名,二期呢,考虑到das产生的字符串有可能太长,修 ...
- ECMAScript 5 新特性
Strict模式 开启strict: 在文件头部,或者在一个function头部内,添加‘use strict’或者“use strict”. Strict模式的限制,以及违反时出现的异常: 新定制了 ...
- 两种方法上传本地文件到github
https://www.jianshu.com/p/c70ca3a02087 自从使用github以来,一直都是在github网站在线上传文件到仓库中,但是有时因为网络或者电脑的原因上传失败.最重要的 ...
- 阿里云邮件服务器怎么设置才能在QQ邮箱访问,互发邮件?
必须要在阿里云邮上打开IMAP和SMTP IMAP能够代发代收.在线更改.垃圾拦截,比POP3好: 记住打开的协议号: IMAP:143 带SSL:993 SMTP: 25 带SSL:465 前提是能 ...
- 【WebApi系列】浅谈HTTP
[01]浅谈HTTP在WebApi开发中的运用 [02]聊聊WebApi体系结构 [03]详解WebApi如何传递参数 [04]详解WebApi测试和PostMan [05]浅谈WebApi Core ...
- [数据分析工具] Pandas 功能介绍(二)
条件过滤 我们需要看第一季度的数据是怎样的,就需要使用条件过滤 体感的舒适适湿度是40-70,我们试着过滤出体感舒适湿度的数据 最后整合上面两种条件,在一季度体感湿度比较舒适的数据 列排序 数据按照某 ...
- FORTH基本堆栈操作
body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...