有时候觉得‘如约而至’是个多么美好的词,等的很辛苦,却不辜负。——《匿名》
1、引言
传统权限管理使用基于url拦截的权限管理方式,实现起来比较简单,不依赖框架,使用web提供filter就可以实现。但是这种方式存在问题,需要将将所有的url全部配置到xml中起来,有些繁琐,不易维护,url(资源)和权限表示方式不规范。
shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。
spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。
shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。使用shiro实现系统的权限管理,有效提高开发效率,从而降低开发成本。
2、shiro架构
subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套 - session管理的方式。
SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。
注意:在realm中存储授权和认证的逻辑。
- cryptography:密码管理,提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。比如 md5散列算法。
3、shiro认证
1、首先构造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.apache.shiro.authc.UnknownAccountException)
如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)
代码演示如下:
shiro-first.ini
#对用户信息进行配置
[user]
#用户账号和密码
zhangsan=111111
lisi=222222
// 用户登陆和退出
@Test
public void testLoginAndLogout() {
// 创建securityManager工厂,通过ini配置文件创建securityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-first.ini");
//创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//将securityManager设置当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);
//从SecurityUtils里边创建一个subject
Subject subject = SecurityUtils.getSubject();
//在认证提交前准备token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "111111");
try {
//执行认证提交
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//是否认证通过
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否认证通过:" + isAuthenticated);
//退出操作
subject.logout();
//是否认证通过
isAuthenticated = subject.isAuthenticated();
System.out.println("是否认证通过:" + isAuthenticated);
}
4、shiro授权
算法流程:
1、构造SecurityManager环境后,对subject进行授权,调用方法isPermitted(”permission串”)
2、SecurityManager执行授权,通过ModularRealmAuthorizer执行授权
3、ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据
调用realm的授权方法:doGetAuthorizationInfo
4、realm从数据库查询权限数据,返回ModularRealmAuthorizer
5、ModularRealmAuthorizer调用PermissionResolver进行权限串比对
6、如果比对后,isPermitted中”permission串”在realm查询到权限数据中,说明用户访问permission串有权限,否则没有权限,抛出异常。
4.1 Shiro 支持三种方式的授权:
- (1)编程式:通过写if/else 授权代码块完成:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
- (2)注解式:通过在执行的Java方法上放置相应
@RequiresRoles("admin")
public void hello() {
//有权限
}
- (3)JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRole name="admin">
<!— 有权限—>
</shiro:hasRole>
4.2 shiro-permission.ini内容
shiro-permission.ini里边的内容相当于在数据库查询。
#用户
[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhang=123,role1,role2
wang=123,role2
#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create
权限标识符号规则:资源:操作:实例(中间使用半角:分隔)
user:create:01 表示对用户资源的01实例进行create操作。
user:create:表示对用户资源进行create操作,相当于user:create:*,对所有用户资源实例进行create操作。
user:*:01 表示对用户资源实例01进行所有操作。