第一节:shiro简介
1. 重点:认识shiro,认识shiro架构及相关对象。
2. 课程实际内容
a) 简介:简单回顾rbac,引出权限管理框架shiro和spring security。
b) 简介:什么是shiro
c) 简介:为什么要学习shiro
d) 重点:shiro架构并将shiro的组成部分和rabc中关键对象进行对比
e) 简介:如何获取shiro相关资源
3. 重点:课堂总结
a) 总结shiro架构中的关键对象。并记忆。知道认证和授权相关的术语。
第二节:shiro认证
1. 重点:讲解认证流程并实现入门程序。
2. 课程实际内容
a) 重点:shiro认证流程
b) 重点:入门程序实现
c) 简介:测试
3. 重点:课堂总结
a) 认证实现
练习20分钟
第三节:分析认证执行流程,及源码分析
1. 重点:认证执行流程及常见异常
2. 课程实际内容
a) 重点:认证执行流程
b) 简介:带领学生看看源码
c) 重点:常见异常讲解及分析
3. 重点:课堂总结
a) 认证执行流程及常见异常
第四节:自定义Realm实现
1. 重点:理解Realm的作用,及实现自定义Realm
2. 课程实际内容
a) 重点:什么是Realm,及其接口
b) 重点:自定义Realm实现
c) 重点:测试
3. 重点:课堂总结
a) 自定义Realm的相关步骤
练习20分钟
第五节:散列算法
1. 重点:shiro中使用散列算法
2. 课程实际内容
a) 简介:散列算法简介
b) 重点:在自定义的Realm中使用md5
c) 重点:配置凭证匹配器
d) 简介:测试
3. 重点:课堂总结
a)shiro中散列算法的使用
第一节课备课参考
核心脉络
回顾rbac,由rbac引出权限框架的必要性。介绍权限框架shiro与spring security。介绍shiro及其架构。
重点:
1. Shiro架构及其相关对象
难点:
1. Shiro架构及其相关对象
互动:之前讲述了rbac权限管理,并实现了其功能。几乎在所有的系统中都会涉及权限问题,在不同的系统中不断重复几乎相同的代码。显然这些相同的部分可以抽取出来形成一套框架,这就是权限管理框架。权限管理框架目前使用最多的是shiro和spring security.
接下来我们开始学习shiro.
shiro介绍
什么是shiro
Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。
为什么要学shiro
既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。
java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。
Shiro架构
Subject
Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权
SecurityManager
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
Authenticator
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
Authorizer
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
realm
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
sessionManager
sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
SessionDAO
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
CacheManager
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
Cryptography
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
Shiro资源获取
地址:http://shiro.apache.org/
shiro的jar包
与其它java开源框架类似,将shiro的jar包加入项目就可以使用shiro提供的功能了。shiro-core是核心包必须选用,还提供了与web整合的shiro-web、与spring整合的shiro-spring、与任务调度quartz整合的shiro-quartz等,下边是shiro各jar包的maven坐标。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.2.3</version>
</dependency>
也可以通过引入shiro-all包括shiro所有的包:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>
参考lib目录:
第二节课备课参考
核心脉络
在上一节中介绍了shiro的架构及相关组件,那么接下来就开始一个shiro的案例。在本次课中,实现shiro的认证
重点:
1. Shiro的认证流程及代码实现
难点:
1. Shiro的认证流程及代码实现
认证基本概念
身份验证
即在应用中谁能证明他就是他本人。一般提供如他们的身份ID一些标识信息来
表明他就是他本人,如提供身份证,用户名/密码来证明。
在 shiro 中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能
验证用户身份:
principals
身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
credentials
证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。接下来先进行一个基本的身份认证。
认证流程
入门程序(用户登陆和退出)
a)创建java工程
b)加入相关jar包
commons-beanutils-1.9.2.jar
commons-logging-1.2.jar
junit-4.10.jar
shiro-all-1.2.3.jar
slf4j-api-1.7.7.jar
log4j-1.2.17.jar
slf4j-log4j12-1.7.5.jar
c)log4j.properties日志配置文件
log4j.rootLogger=debug, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n |
d)配置shiro环境文件shiro.ini
通过Shiro.ini配置文件初始化SecurityManager环境。
[users] zhangsan=1111 lisi=1111 |
e)代码实现
//用户登录和退出 @Test public void testAuthenticator(){ // 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //通过工厂获得SecurityManager实例 SecurityManager securityManager = factory.getInstance(); //将securityManager设置到运行环境中 SecurityUtils.setSecurityManager(securityManager); //获取subject实例 Subject subject = SecurityUtils.getSubject(); //创建用户名,密码身份验证Token UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "1111"); try { //登录,即身份验证 subject.login(token); } catch (AuthenticationException e) { e.printStackTrace(); //身份认证失败 } //断言用户已经登录 Assert.assertEquals(true, subject.isAuthenticated()); //退出 subject.logout(); } |
测试
第三节课备课参考
核心脉络
分析认证执行流程,分析源码,进行核心代码解读。讲解常见异常
重点:
1. 认证执行流程
难点:
1. 分析源码
互动:接下来,我们一起来看看高大上的东西----源码。通过源码的分析大家可以对shiro的底层原理深层次的搞清楚
认证执行流程
1、 创建token令牌,token中有用户提交的认证信息即账号和密码
2、 执行subject.login(token),最终由securityManager通过Authenticator进行认证
3、 Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro自带)
4、 IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
常见的异常
n UnknownAccountException
账号不存在异常如下:
org.apache.shiro.authc.UnknownAccountException: No account found for user。。。。
n IncorrectCredentialsException
当输入密码错误会抛此异常,如下:
org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token[org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] didnot match the expected credentials.
更多如下:
DisabledAccountException(帐号被禁用)
LockedAccountException(帐号被锁定)
ExcessiveAttemptsException(登录失败次数过多)
ExpiredCredentialsException(凭证过期)等
第四节课备课参考
核心脉络
回顾shiro架构图,讲解realm的作用,以及自定义Realm的必要性。
重点:
1. 自定义Realm
难点:
1. 自定义Realm
互动:在分析了源码以后,那么接下来看看shiro给我们提供了很多自定义的空间来实现我们自己要想的功能。其中最常用的自定义组件是Realm.
什么是Realm
Realm 是可以访问程序特定的安全数据如用户、角色、权限等的一个组件。Realm会将这些程序特定的安全数据转换成一种shiro可以理解的形式,shiro就可以依次提供容易理解的Subject程序API而不管有多少数据源或者程序中你的数据如何组织。
Realm 通常和数据源如数据库、LDAP目录、文件系统或者其它类似的数据源是一对一的关系,所以,可以用数据源相应的API如JDBC、File IO、 hibernate 或者JPA以及其它的API来实现Realm接口,从而获取授权的相关数据(角色、权限等)。
realm本质上就是一个指定安全的DAO。
自定义Realm
Shiro默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。
Realm接口
最基础的是Realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthorizingRealm负责授权,通常自定义的realm继承AuthorizingRealm。
自定义Realm实现
/** * 自定义Realm实现 * @author邹波 * @version 1.0 * @date 2016-1-21 */ public class UserRealm extends AuthorizingRealm { @Override public String getName() { return "UserRealm"; } //用于认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //从token中获取身份信息 String username = (String)token.getPrincipal(); //根据用户名到数据库中取出用户信息 如果查询不到 返回null String password = "1111";//假如从数据库中获取密码为1111 //返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, this.getName()); return simpleAuthenticationInfo; } //用于授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { return null; } } |
配置Realm
需要在shiro.ini配置realm注入到securityManager中。
[main] #自定义 realm userRealm=cn.siggy.realm.UserRealm #将realm设置到securityManager securityManager.realms=$userRealm |
测试
第五节课备课参考
核心脉络
回顾在认证流程中对于密码的处理。阐述密码加密的必要性和重要性。引出散列算法。
重点:
1. Shiro中散列算法的使用
难点:
1.Shiro中散列算法的使用
散列算法
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如
加密密码“admin”,产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,可以到一
些md5 解密网站很容易的通过散列值得到密码“admin”,即如果直接对密码进行散列相
对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和ID(即盐);
这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解。
MD5算法
/** * * @author邹波 * @version 1.0 * @date 2016-1-21 */ public class ShiroTest { //shiro提供了现成的加密类 Md5Hash @Test public void testMd5(){ //MD5加密 String password = new Md5Hash("1111").toString(); System.out.println("加密后:"+password); //加盐 salt 默认一次散列 String password_salt=new Md5Hash("1111", "siggy").toString(); System.out.println("加盐后:"+password_salt); //散列2次 String password_salt_2 = new Md5Hash("1111", "siggy", 2).toString(); System.out.println("散列2次:"+password_salt_2); //使用SimpleHash SimpleHash hash = new SimpleHash("MD5", "1111", "siggy", 2); System.out.println("simpleHash:"+hash.toString()); } } |
在自定义Realm中使用散列
Realm实现代码
public class UserRealm extends AuthorizingRealm { @Override public String getName() { return "UserRealm"; } //用于认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //从token中获取身份信息 String username = (String)token.getPrincipal(); //根据用户名到数据库中取出用户信息 如果查询不到 返回null //按照固定规则加密码结果 ,此密码 要在数据库存储,原始密码 是1111,盐是siggy 2次散列 String password = "1620d20433da92e2523928e351e90f97";//假如从数据库中获取密码为1111 //返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes("siggy"),this.getName()); return simpleAuthenticationInfo; } //用于授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { return null; } } |
Realm配置
Shiro.ini 在配置文件中,需指定凭证匹配器
[main] #定义凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #散列算法 credentialsMatcher.hashAlgorithmName=md5 #散列次数 credentialsMatcher.hashIterations=2
#将凭证匹配器设置到realm userRealm=cn.siggy.realm.UserRealm userRealm.credentialsMatcher=$credentialsMatcher securityManager.realms=$userRealm |
测试