一、前言
前面我们通过url拦截器实现了权限验证,功能齐全,开发难度也不大。但是为了更好的进行开发权限,开发我们选择shrio,终于扯上正题了是不是?当你学习shrio你会发现,都是一样的。嘎嘎
shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。简单好用用的人就多了哦。
二、shrio基础
首先一样来个图:
2.1看图说话
定要细心品味,结合url拦截器权限。。。
subject:用户或者程序,可以是任何语言编写。(这个跟uel拦截器一样)
securityManager:安全管理器,对全部的
subject
进行安全管理,它是
shiro
的核心,负责对所有的
subject
进行安全管理。通过
SecurityManager
可以完成
subject
的认证、授权等,实质上
SecurityManager
是通过
Authenticator
进行认证,通过
Authorizer
进行授权,通过
SessionManager
进行会话管理等。它自身只是一个接口。
Authentic:认证器,
Authenticator是一个接口,
shiro
提供
ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自 定义认证器。(相当于ur权限l的登录拦截器哦)
Authorizer:
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限(相当于url权限系统的权限拦截器)。
sessionManager:
即会话管理,
shiro
框架定义了一套会话管理,它不依赖
web
容器的
session
,所以
shiro
可以使用在非
web
应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。(相当于ur权限系统里面自带的httpsession,而它可以独立)
sessionDao:
SessionDAO即会话
dao
,是对
session
会话操作的一套接口,比如要将
session
存储到数据库,可以通过
jdbc
将会话存储到数据库。
realms:领域相
当于
datasource
数据源,
securityManager
进行安全认证需要通过
Realm
获取用户权限数据,比如:如果用户身份数据在数据库那么
realm
就需要从数据库获取用户身份信息。(相当于ur权限系统里面自带的mysql,而它还支持验证)
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
cacheManager: CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
Cryptography:Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。MD5即是散列(相当于ur权限系统里面MD5)
二、shrio的jar包
<!--======================================shrio=================================================--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.4</version> </dependency>
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.4</version> </dependency>
三、shrio的ini认证程序实例
项目分析
3.1加入jar包
加入上面的jar包core包即可以
再加入日志文件与包
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency>
3.3 编写ini文件
shrio-frist.ini
#用户信息配置 [users] zhangsan=111 ycy=222
3.4 编写测试类
我们根据项目流程一步步来编写我们的测试程序,
即是:读取ini或者xml文件----创建securityManager----加入运行环境----生成一个sbject---登录(加入taken)---退出
- 1、通过ini配置文件创建securityManager
- 2、调用subject.login方法主体提交认证,提交的token
- 3、securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。
- 4、ModularRealmAuthenticator调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息
- 5、IniRealm根据输入的token(UsernamePasswordToken)从 shiro-first.ini查询用户信息,根据账号查询用户信息(账号和密码)
- 如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)
- 如果查询不到,就给ModularRealmAuthenticator返回null
- 6、ModularRealmAuthenticator接收IniRealm返回Authentication认证信息 如果返回的认证信息是null,ModularRealmAuthenticator抛出异常(org.apach e.shiro.authc.UnknownAccountException)
- 如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)
- 注意:1、是realm调用token信息,进行验证。
- 2、securityManager是调用ModularRealmAuthenticator进行认证返回AuthenticationInfo认证信息
authenticationTest.java
package co.ycy.authenticator; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.util.Factory; import org.junit.Test; /** * Created by Administrator on 2015/10/15 0015. * 认证测试 */ public class authenticationTest { //用户登录和退出 @Test public void testLoginAndLoginOut(){ //创建一个securityManager 通过ini文件创建 Factory<SecurityManager> securityManagerFactory=new IniSecurityManagerFactory("classpath:shrio-frist.ini"); // XMLSecurityManager //创建SecurityManager SecurityManager securityManager= securityManagerFactory.getInstance(); //将SecurityManager创建到生成环境中 SecurityUtils.setSecurityManager(securityManager); //从SecurityUtils 构建一个subject org.apache.shiro.subject.Subject subject=SecurityUtils.getSubject(); //认证提交认证token UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("ycy","222"); //执行认证提交认证 try { subject.login(usernamePasswordToken); }catch(AuthenticationException ex){ ex.printStackTrace(); } System.out.println("是否通过认证:" + subject.isAuthenticated()); //退出 subject.logout(); System.out.println("是否通过认证:" + subject.isAuthenticated()); } }3.5 测试结果
我们知道realm就是数据源,即是我们经常说的各种数据库,但是我们前面例子用的是ini文件,我们现在来模拟一下用数据。(模拟)
四、shrio的ini认证-realms自定义
4.1编写自定义realm
自定义realm,继承我们的AuthorizingRealm。因为AuthorizingRealm就是我们realm的一个实现类
<span style="font-size:12px;">package com.ycy.realm; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; /** * Created by Administrator on 2015/10/15 0015. * 自定义数据源 */ public class CustomRealm extends AuthorizingRealm{ //用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //authenticationToken为用户输入信息 String userCode=(String)authenticationToken.getPrincipal(); //模拟数据库操作查询用户 if(!userCode.equals("ycy")){ return null; } //查询密码为111 String password="222"; SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(userCode,password,"customRealm"); return info; } //用户授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } }</span>
4.1ini文件
这个ini文件是制定我们的realms,而不再是指定数据
shrio-realm.ini
#用户信息配置 [main] #自定义realm cutomRealm= com.ycy.realm.CustomRealm #自定义realm注入到securityManager中(标签语言) securityManager.realms=$cutomRealm
4.3 测试
测试程序一样没有变,只是现在执行认证的时候进行的是我们自己定义的realm的认证
//用户登录和退出(自定义realm) @Test public void testCustomRealm(){ //创建一个securityManager 通过ini文件创建 Factory<SecurityManager> securityManagerFactory=new IniSecurityManagerFactory("classpath:shrio-realm.ini"); // XMLSecurityManager //创建SecurityManager SecurityManager securityManager= securityManagerFactory.getInstance(); //将SecurityManager创建到生成环境中 SecurityUtils.setSecurityManager(securityManager); //从SecurityUtils 构建一个subject org.apache.shiro.subject.Subject subject=SecurityUtils.getSubject(); //认证提交认证token UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("ycy","222"); //执行认证提交认证 try { subject.login(usernamePasswordToken); }catch(AuthenticationException ex){ ex.printStackTrace(); } System.out.println("是否通过认证:" + subject.isAuthenticated()); //退出 subject.logout(); System.out.println("是否通过认证:" + subject.isAuthenticated()); }
测试结果一样,就不用贴图了。
四、shrio的ini认证-realms自定义(加密)
只需要在realm里面写入加密就ok
4.1了解shrio的MD5加密
package com.ycy.test; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.crypto.hash.SimpleHash; /** * Created by Administrator on 2015/10/15 0015. */ public class MD5Test { public static void main(String[] args) { //原始密码 String source="111"; //盐 String salt="ycy"; //散列次数 int hashInterration=1;//MD5(****)一次 Md5Hash md5Hash=new Md5Hash(source,salt,hashInterration); String password_md5=md5Hash.toString(); System.out.println("111加盐MD5加密后为:"+password_md5); //散列加密2 SimpleHash simpleHash=new SimpleHash("md5",source,salt,hashInterration); System.out.println("111加盐MD5加密后为:"+simpleHash.toString()); //96c26c8872da9d2534bf7c31f499425d } }
4.2 修改ini文件
[main] #定义凭证匹配 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #定义散列算法 credentialsMatcher.hashAlgorithmName=md5 #散列次数 credentialsMatcher.hashIterations=1 #自定义realm customRealm= com.ycy.realm.CustomRealm2 customRealm.credentialsMatcher=$credentialsMatcher securityManager.realms=$customRealm
4.3修改自定义realm
package com.ycy.realm; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; /** * Created by Administrator on 2015/10/15 0015. * 自定义数据源 */ public class CustomRealm2 extends AuthorizingRealm{ //用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //authenticationToken为用户输入信息 String userCode=(String)authenticationToken.getPrincipal(); //模拟数据库操作查询用户 if(!userCode.equals("ycy")){ return null; } //查询密码为111 //数据库111 MD5加盐ycy 加密后为96c26c8872da9d2534bf7c31f499425d String password="96c26c8872da9d2534bf7c31f499425d"; String salt="ycy"; SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(userCode,password, ByteSource.Util.bytes(salt),"customRealm"); return info; } //用户授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } }
4.4测试
//用户登录和退出(自定义realm)加密 @Test public void testCustomRealmMd5(){ //创建一个securityManager 通过ini文件创建 Factory<SecurityManager> securityManagerFactory=new IniSecurityManagerFactory("classpath:shrio-realm-md5.ini"); // XMLSecurityManager //创建SecurityManager SecurityManager securityManager= securityManagerFactory.getInstance(); //将SecurityManager创建到生成环境中 SecurityUtils.setSecurityManager(securityManager); //从SecurityUtils 构建一个subject org.apache.shiro.subject.Subject subject=SecurityUtils.getSubject(); //认证提交认证token UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("ycy","111"); //执行认证提交认证 try { subject.login(usernamePasswordToken); }catch(AuthenticationException ex){ ex.printStackTrace(); } System.out.println("是否通过认证:" + subject.isAuthenticated()); //退出 subject.logout(); System.out.println("是否通过认证:" + subject.isAuthenticated());