什么是Shiro?
Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。Shiro可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。
Shiro架构有三个主要概念, Subject, SecurityManager, Realms
Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager:是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是文件实现,或者内存实现等等;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
SessionManager:如果写过Servlet就应该知道Session的概念,Session需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境等环境;Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;可以实现分布式的会话管理;
SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到redis中,可以实现自己的redis SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
下面是Shiro的一个简单登录认证Demo(JavaSE环境)
首先了解一下认证的流程分析:
1、调用subject.login方法进行登录,其会自动委托给securityManager.login方法进行登录;
2、securityManager通过Authenticator(认证器)进行认证;
3、Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro自带,相当于数据源);
4、IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
5、最后调用Subject.logout进行退出操作。
认证操作:
模拟准备一些用户
代码实现:
用户名不存在时抛出的异常:
密码错误时抛出的异常:
认证操作源码解析:
自定义Realm:
Realm仅仅是提供数据给认证器做相关的一些判断,本身自己不会做密码判断,因为有对应的凭证匹配器去做判断.
在Shiro框架中提供了两个加密工具
在项目中使用时需要添加配置文件, 定义一个凭证匹配器,注入到Realm, 再把Realm设置到SecurityManager上面就可以生效了,
授权流程:和认证流程大体相似,核心都是要构造SecurityManager环境
授权方式主要有三种:编程式, 注解式, JSP标签
第一种: 通过写if/else授权代码块完成
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
第二种: 通过在执行的Java方法上放置相应的注解完成
@RequiresRoles("admin")
public void hello() {
//有权限
}
第三种 : 在JSP页面通过相应的标签完成
权限字符串规则:
在ini文件中用户、角色、权限的配置规则是:“用户名=密码,角色1,角色2...” “角色=权限1,权限2...”,首先根据用户名找角色,再根据角色找权限,角色是权限集合。
权限字符串的规则是:“资源标识符:操作:资源实例标识符”,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用*通配符。
例子:
用户创建权限:user:create,或user:create:*
用户修改实例001的权限:user:update:001
用户实例001的所有权限:user:*:001
操作步骤:
1.使用ini的方式来配置用户,角色,权限
2.在认证成功之后进行权限/角色判断
常用的几种判断方式
1.补充自定义realm的授权方法
2.在认证后进行角色或权限的验证
授权流程分析:
1、首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer
2、Authorizer是真正的授权者
3、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限;
4、Authorizer会判断Realm的角色/权限是否包含该权限,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。