一.Shiro简介
Shiro框架是和spring security框架作用差不多的一个安全认证授权框架,但它更加的轻便和简单,越来越多的企业在使用它进行角色权限,安全认证等方面功能的管理.
二.主要架构阅览

Subject 主体,既可以代表用户,也可以代表程序(网络爬虫等),它需要访问系统,系统则需要对其进行认证和授权.
SecurityManager 安全管理,用户请求Url,对应于一个Subject对象,由SecurityManager统一对Subject进行认证和授权.(父)
Authenricator 认证部分,主要对Subject进行认证,Subject的信息在shrio中是通过AuthenticationToken对象来储存,由Authenrication进行验证管理.(子)
Authorizer 授权部分,Subject认证后,由它来对其授予对应角色权限.(子)
SessionManager Shiro的session管理方式,Shiro提供了一个专门管理session的方式,通常的web程序中的session是HttpSession的对象,是由web容器来管理的.
SessionDao session的接口,Shiro通过它来管理session数据,个性化的session数据储存需要使用sessionDao.
CacheManager 缓存管理工具,主要对session数据和授权数据进行缓存,减小数据库的访问压力.可以通过和ehcache的整合对缓存数据进行管理.
Pluggable Realms 可扩展领域,相当于数据源,我们通过上面内容可以大致了解到Shiro的工作原理,但Shiro是怎样得知Subject的信息和数据库的信息是否匹配呢?Shiro这里就提供了一个realms的概念,它的作用就是得到数据库中的信息.这个realm是可以多个并且可以自定义,只需继承AuthorizingRealm这个接口就可以了.
注意:对Subject进行认证和授权都需要调用realm,所以realm不仅仅相当于数据源,更加包含了认证和授权的一种逻辑.
Cryptography 密码演算法,一个密码管理工具,提供了一套加密/解密的组件.比如长用的散列,加/解密等功能,日常练习所使用的md5算法其实是一种散列算法,只能加密,不能解密.
可见Shrio的核心部分还是认证和授权部分,其他都是围绕这俩部分进行的,只需理解了这两部分就可以进行开发了.
三.Shiro认证实例(入门案例)
Shiro处理一个Subject流程图

Shiro处理认证流程图,相当于上图的扩充和细化

可见,Shiro处理流程是一级一级的调用,主要是调用Authentication来进行验证,最后还是需要使用realm来进行身份验证(realm后面会提).
有了基本的执行流程图,大概就能懂了Shiro的运行原理,接下来实践一下,首先新建一个普通的java工程,导入shiro-core.jar包

这个作为入门程序导入这一个核心包就行了,建立测试代码,我这里用的是junit,也可以直接main()走起.代码如下:
//模拟用户登录登出
@Test
public void demo1(){
//创建SecurityManager工厂,生成SecurityManager环境(通过shrio的ini文件), Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shrio.ini");
//创建SecurityManager
SecurityManager manager= factory.getInstance();
//将当前环境设为SecurityManager
SecurityUtils.setSecurityManager(manager);
//模拟Subject
Subject subject = SecurityUtils.getSubject();
//提交认证是说携带的信息储存在 token 中
UsernamePasswordToken token = new UsernamePasswordToken("Floder", "Floder");
try {
//模拟获取登陆
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//判断是否认证通过
boolean isOk = subject.isAuthenticated();
System.out.println("用户是否登录:"+isOk);
//模拟用户退出登陆
subject.logout();
isOk = subject.isAuthenticated();
System.out.println("用户是否登录:"+isOk);
}
这里有几点要说明的,
1.这里需要配置Shiro的配置文件,我看了一下源码,是通过InputStream来扫描配置文件,所以文件名任意.需要表明路径即可,classpath即为项目的src目录下,Shiro.ini的配置如下
#自定义数据源 格式 用户名=密码
[users]
Floder=Floder
2.构建工厂时Factory的泛型SecurityManager需要是用的是SecurityManager的接口,因为java.lang.SecurityManager也存在...
Shiro授权流程图

和认证流程图差不多,不过这个调用的是Authrizer模块
在进行实际操作前,需要理解Shiro的两个重要概念
1.基于角色管理:根据用户的相应角色授予相应的资源权限,但这样做有点不好的地方就是每新增一个角色都要对角色进行相应的资源权限管理(超级QQ,QQ会员),在实际开发中太过麻烦,不经常使用.
2.基于资源管理:对资源的访问需要一定的权限,这样的话每新增一个角色只需在权限对应的资源进行角色新增即可.特别方便.
测试代码:
//模拟用户认证授权
@Test
public void testAuthorizer(){
//创建SecurityManager工厂,生成SecurityManager环境(通过shrio的ini文件),//通过ini配置文件创建
securityManagerFactory factory = new IniSecurityManagerFactory("classpath:shrio-permission.ini");
//创建SecurityManager
SecurityManager manager= factory.getInstance();
//将当前环境设为SecurityManager
SecurityUtils.setSecurityManager(manager);
//模拟Subject
Subject subject = SecurityUtils.getSubject();
//提交认证是说携带的信息储存在 Token 中
UsernamePasswordToken token = new UsernamePasswordToken("Floder", "Floder");
//模拟获取登陆
try {
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//基于角色判断
//判断已认证用户是否拥有role1角色
boolean isHasRole = subject.hasRole("role2");
System.out.println("判断已认证用户是否拥有role2角色 "+isHasRole);
//也可以判断已认证用户是否有多个角色
boolean isHasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2"));
System.out.println("判断已认证用户是否有多个角色"+isHasAllRoles);
//基于资源判断
//判断已认证用户的资源是否拥有对应单个的操作
boolean isPermitted = subject.isPermitted("user:create");
System.out.println("判断已认证用户的资源是否拥有对应的单个操作 "+isPermitted);
//判断已认证用户的资源是否拥有对应的多个操作
boolean isPermittedAll = subject.isPermittedAll("user:create","user:update");
System.out.println("判断已认证用户的资源是否拥有对应的多个操作 "+isPermittedAll);
}
这里需要新建一个Shrio-permission.ini配置文件,具体信息如下:
#用户对应的角色
#用户Floder拥有角色role1和role2
[users]
Floder = Floder,role1,role2
floder=floder,role2
#角色对应的资源权限
[roles]
#权限标识符符号规则 资源:操作:实例 user:create:01 表示对用户资源实例01进行create操作
#user:create 表示对资源进行create操作,相当于user:create:*
#user:*:01 表示对用户资源实例01进行所有操作
role1=user:create,user:update
role2 = user:create
和Shrio认证一样,只不过这里的user新增的角色栏,另外还对[roles]进行了相应的权限设置,看代码即可,注释都写的清清楚楚
通过这两个认证和授权实例,大概能理解了ini文件起的作用,一般是相当于数据源,在Shiro中就是Realm,注意,Realm不仅仅起数据源的作用,更加包含了认证和授权的一种逻辑.重要的事说两遍
四.自定义Realm实现
在上一节中我们并没有自定义realm,那它是怎样调用认证和授权的一种逻辑呢?原来,Shiro默认提供了一个IniRealm实现,但我们知道,可以自定义Realm实现来实现我们的需求,只需实现AuthorizingRealm接口就行了(常用是这个),Realm有多种接口:
Realm结构图

具体继承那个接口看需求,我们测试的时候就使用AuthorizingRealm
自定义UserRealm:
//设置realm名,需要继承setName()方法
@Override
public void setName(String name) {
super.setName("userName");
}
//认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//得到token的用户信息
String userId = (String) token.getPrincipal();
//模拟数据库返回数据,如果不存在就返回null
//.......
//模拟数据库返回数据,存在就返回一个credentials
String password = "Floder";
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userId, password, this.getName());
return info;
}
//授权方法(依赖于上面的认证信息)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
String userId = (String) principals.getPrimaryPrincipal();
//模拟从数据库得来的角色信息,
List<String> listPermission = new ArrayList<String>();
//模拟用户创建权限
listPermission.add("user:create");
//模拟用户添加权限
listPermission.add("user:update");
//这里的info使用的是SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//SimpleAuthorizationInfo携带的是数据库返回的信息,数据实现的是collection接口的玩意info.addStringPermissions(listPermission);
return info;
}
这里如果需要模拟null就很简单了,写个if()判断即可,这里的info信息我们实现的是SimpleAuthenticationInfo接口和SimpleAuthenticationInfo授权接口,
我们知道realm是与数据库打交道的,由于有了自定义realm,我们就不用在ini文件中写[users]了,所以修改Shiro.ini文件的信息:
[main]
#声明我们自定义的realm
userRealm=com.floder.junit.realm.UserRealm
#将realm放入SecurityManager中
SecurityManager.realms=$userRealm
好了,准备工作都完成了,接下来开始测试,认证登陆模拟和上一节模拟登陆代码相同,授权测试代码需要修改部分代码:
try {
//模拟获取登陆
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//基于资源判断
//判断已认证用户的资源是否拥有对应的操作
boolean isPermitted = subject.isPermitted("user:create");
System.out.println("判断已认证用户的资源是否拥有对应的操作 "+isPermitted);
//使用check方法进行授权,如果不通过则抛出异常
subject.checkPermission("item:add");//这是需要添加的,基于角色就不需要测试了
注释这么清楚,就不解释了.
网友评论