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 ...
随机推荐
- hdu_1042(模拟大数乘法)
计算n! #include<cstring> #include<cstdio> using namespace std; ]; int main() { int n; whil ...
- 96、python version 3.6 required,which was not fount in the registry(python3.6安装scrapy)
在安装scrapy时遇到问题 环境:win10(64位), Python3.6(64位) 安装scrapy: 1.安装wheel(安装后,便支持通过wheel文件安装软件) pip3 install ...
- [国嵌攻略][157][SPI总线介绍]
SPI总线架构 SPI(serial peripheral interface)串行外设接口,是一种高速,全双工,同步的通信总线.采用主从模式(master slave)架构,支持多个slave,一般 ...
- JavaScript实现职责链模式
什么是职责链模式 职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.举个例子:当你从公 ...
- NGINX 配置404错误页面转向
什么是404页面 如果碰巧网站出了问题,或者用户试图访问一个并不存在的页面时,此时服务器会返回代码为404的错误信息,此时对应页面就是404页面.404页面的默认内容和具体的服务器有关.如果后台用的是 ...
- 《并行程序设计导论》——Pthreads
这部分不需要看了. 因为C++11和BOOST比这个Pthreads要好一点. 如果不考虑移植性,在Windows平台上用核心编程的东西比C++11和BOOST更好控制.
- IDEA、Matlab 注释
IDEA ctrl+/ 多行代码分行注释,再按一次取消 ctrl+shift+/ 多行代码注释在一个块里,只在开头和结尾有注释符号 Matlab 注释:Ctrl+/ Ctrl+R 取消注释:Ctrl+ ...
- Angular 4+ HttpClient
个人博客迁移至 http://www.sulishibaobei.com 处: 这篇,算是上一篇Angular 4+ Http的后续: Angular 4.3.0-rc.0 版本已经发布
- Postgresql中string转换成timestamp类型
Mybatis+Postgresql TO_DATE(#{startTime}, 'YYYY-MM-DD') AND op_date <![CDATA[>= ]]> TO_TIME ...
- ublime Text 3安装与使用
ublime Text 3安装与使用 工具 2015-07-30 10:46 0 34 工欲善其事,必先利其器.好的工具帮助我们节省大量的工作时间,好用的插件使工具更强大. 1. 下载 可以从官网 h ...