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.xml
Spring配置文件必须含有与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.ShiroFilterFactoryBean
Spring配置文件中配置
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 ...
随机推荐
- 【Java学习笔记之十四】Java中this用法小节
用类名定义一个变量的时候,定义的只是一个引用,外面可以通过这个引用来访问这个类里面的属性和方法. 那们类里面是够也应该有一个引用来访问自己的属性和方法纳? 呵呵,JAVA提供了一个很好的东西,就是 t ...
- 模板类-bitset
stl提供了std::bitset模板类,定义:bitset <32> bitvec;尖括号中的为长度,这条语句把bitvec定义为含有32个的bitset对象.和容器一样,按位置来访问他 ...
- c语言sizeof用法(32位机)
- CentOS 6.5 编译安装 LNMP环境
建立一个软件包目录存放 mkdir -p /usr/local/src/ 清理已经安装包 rpm -e httpd rpm -e mysql rpm -e php yum -y remove http ...
- 小白的Python之路 day5 模块XML特点和用法
模块XML的特点和用法 一.简介 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今 ...
- 浅析const、let与var
以前无论声明变量还是常量,总是使用var一勺端,知道接触了es6之后,发现原来变量.常量的声明其实是很讲究的. 这里简单来谈谈var.const与let. 1.var.var声明的变量没有块级作用域, ...
- 获取屏幕宽高度与可视区域宽高度(availWidth、clientWidth、width、innerWidth)
经常会遇到需要获取屏幕宽度.高度,可视区域宽度.高度等问题,也就常跟这几个打交道,一不小心,还真爱弄混淆了. 先来列举下这几个吧: screen.availHeight.screen.availWid ...
- Spark算子--foreach和foreachPartition
转载请标明出处http://www.cnblogs.com/haozhengfei/p/6776fe93f754daf60d00d2cb509422a1.html foreach和foreachPar ...
- Solr学习笔记2(V7.2)---导入自己的数据
学而不思则罔,思而不学则殆,总是看文档不动手效果是不好的.没有实地的从自己的数据库获取数据测试一下始终是空,总结一下自己的操作步骤吧. 第一步准备配置文件 E:\Solr\server\solr\co ...
- API接口签名验证2
http://www.jianshu.com/p/d47da77b6419 系统从外部获取数据时,通常采用API接口调用的方式来实现.请求方和�接口提供方之间的通信过程,有这几个问题需要考虑: 1.请 ...