阅读源码有助于陶冶情操,本文旨在简单的分析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. HDU 1728 逃离迷宫(DFS经典题,比赛手残写废题)

    逃离迷宫 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. POJ_1064 二分搜索

    /*POJ 1064 *题目大意:有N条绳子,他们的长度分别为Li,如果从他们中切割K条长度相同的绳子的话,这K条绳子每条最长能有多长?答案保留到小数点后2位 *算法分析:这个问题用二分搜索可以非常容 ...

  3. 打造自己的 JavaScript 武器库(转自SegmentFault公众号)

    2017-11-14 SlaneYang SegmentFault 自己打造一把趁手的武器,高效率完成前端业务代码. 前言 作为战斗在业务一线的前端,要想少加班,就要想办法提高工作效率.这里提一个小点 ...

  4. 【深度学习系列】PaddlePaddle可视化之VisualDL

    上篇文章我们讲了如何对模型进行可视化,用的keras手动绘图输出CNN训练的中途结果,本篇文章将讲述如何用PaddlePaddle新开源的VisualDL来进行可视化.在讲VisualDL之前,我们先 ...

  5. The most interesting feature of iPhone X - FaceID

    No doubt everybody knows that iPhone 8 & iPhone X appear on the market. A feature called FaceID ...

  6. maven项目 在eclipse,InteliJ IDEA中的一些问题

    转载请注明出处,谢谢! 不论我们用什么ide来编辑我们的代码,最终的产品都会脱离ide来运行:正如燕飞离了巢,正如你离开了家,不期然就会运转出现问题. - 单强 2018年1月26日11:53 大家是 ...

  7. 随手小代码——《Python编程 从入门到实践》项目1:外星人入侵

    =================================版权声明================================= 版权声明:原创文章 禁止转载  请通过右侧公告中的“联系邮 ...

  8. Anaconda入门教程

    Anaconda入门教程 [TOC] Anaconda是什么 Anaconda附带了 conda.Python 和 150 多个科学包及其依赖项.应用程序 conda 是包和环境管理器.Anacond ...

  9. UILabel的顶对齐解决方法

    对于有多行文字的UILabel而言,需要设置UILabel的numberoflines属性,此属性默认是1,也就是只显示一行,多余的会以尾部,中间的方式进行截断,具体要看你的初始设置. 在这里可以将其 ...

  10. postgresql+mybatis返回值是数据库字段名

    mybatis 返回map的时候是下划线 role_id, user_id 两种解决方法 1.重命名 postgresql不支持驼峰 加上双引号重命名  SELECT role_id "ro ...