阅读源码有助于陶冶情操,本文旨在简单的分析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.xmlDelegatingFilterProxy类对应的<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;
}

总结

  1. Spring容器中使用Apache Shiro,需要配置

    • web.xml中配置节点,节点类为org.springframework.web.filter.DelegatingFilterProxy
    • spring配置shiro文件中需要节点,节点id名必须与web.xml定义中的节点的属性一致,且beanClass为org.apache.shiro.spring.web.ShiroFilterFactoryBean
  2. Spring配置文件中配置org.apache.shiro.spring.web.ShiroFilterFactoryBean主要设置shiro的相关filter用于拦截请求,其中的属性含义见本文的示例文件说明

下节预告

Spring-shiro源码陶冶-DefaultFilter

Spring-shiro源码陶冶-DelegatingFilterProxy和ShiroFilterFactoryBean的更多相关文章

  1. Spring-shiro源码陶冶-DefaultFilter

    阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用 简单介绍 Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能 Apache Shiro自带的 ...

  2. shiro源码篇 - shiro的filter,你值得拥有

    前言 开心一刻 已经报废了一年多的电脑,今天特么突然开机了,吓老子一跳,只见电脑管家缓缓地出来了,本次开机一共用时一年零六个月,打败了全国0%的电脑,电脑管家已经对您的电脑失去信心,然后它把自己卸载了 ...

  3. Shiro 源码分析

    http://my.oschina.net/huangyong/blog/215153 Shiro 是一个非常优秀的开源项目,源码非常值得学习与研究. 我想尝试做一次 不一样 的源码分析:源码分析不再 ...

  4. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

  5. Spring mybatis源码篇章-MybatisDAO文件解析(一)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...

  6. Spring mybatis源码篇章-NodeHandler实现类具体解析保存Dynamic sql节点信息

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource SqlNode接口类 publi ...

  7. Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(二) 首先了解下sql mapper的动态sql语法 具体的动态sql的 ...

  8. Spring mybatis源码学习指引目录

    前言: 分析了很多方面的mybatis的源码以及与spring结合的源码,但是难免出现错综的现象,为了使源码陶冶更为有序化.清晰化,特作此随笔归纳下分析过的内容.博主也为mybatis官方提供过pul ...

  9. Springboot security cas源码陶冶-ExceptionTranslationFilter

    拦截关键的两个异常,对异常进行处理.主要应用异常则跳转至cas服务端登录页面 ExceptionTranslationFilter#doFilter-逻辑入口 具体操作逻辑如下 public void ...

随机推荐

  1. 【Java学习笔记之十四】Java中this用法小节

    用类名定义一个变量的时候,定义的只是一个引用,外面可以通过这个引用来访问这个类里面的属性和方法. 那们类里面是够也应该有一个引用来访问自己的属性和方法纳? 呵呵,JAVA提供了一个很好的东西,就是 t ...

  2. 模板类-bitset

    stl提供了std::bitset模板类,定义:bitset <32> bitvec;尖括号中的为长度,这条语句把bitvec定义为含有32个的bitset对象.和容器一样,按位置来访问他 ...

  3. c语言sizeof用法(32位机)

  4. CentOS 6.5 编译安装 LNMP环境

    建立一个软件包目录存放 mkdir -p /usr/local/src/ 清理已经安装包 rpm -e httpd rpm -e mysql rpm -e php yum -y remove http ...

  5. 小白的Python之路 day5 模块XML特点和用法

    模块XML的特点和用法 一.简介 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今 ...

  6. 浅析const、let与var

    以前无论声明变量还是常量,总是使用var一勺端,知道接触了es6之后,发现原来变量.常量的声明其实是很讲究的. 这里简单来谈谈var.const与let. 1.var.var声明的变量没有块级作用域, ...

  7. 获取屏幕宽高度与可视区域宽高度(availWidth、clientWidth、width、innerWidth)

    经常会遇到需要获取屏幕宽度.高度,可视区域宽度.高度等问题,也就常跟这几个打交道,一不小心,还真爱弄混淆了. 先来列举下这几个吧: screen.availHeight.screen.availWid ...

  8. Spark算子--foreach和foreachPartition

    转载请标明出处http://www.cnblogs.com/haozhengfei/p/6776fe93f754daf60d00d2cb509422a1.html foreach和foreachPar ...

  9. Solr学习笔记2(V7.2)---导入自己的数据

    学而不思则罔,思而不学则殆,总是看文档不动手效果是不好的.没有实地的从自己的数据库获取数据测试一下始终是空,总结一下自己的操作步骤吧. 第一步准备配置文件 E:\Solr\server\solr\co ...

  10. API接口签名验证2

    http://www.jianshu.com/p/d47da77b6419 系统从外部获取数据时,通常采用API接口调用的方式来实现.请求方和�接口提供方之间的通信过程,有这几个问题需要考虑: 1.请 ...