Shrio权限管理框架
1.权限管理相关概念
1.1 权限管理概述
权限管理:凡是涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可访问而且只能访问自己已被授权的资源。
权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源,用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。
1.2 用户身份认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核,对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件 Key 等刷卡系统,则需要刷卡。
用户身份认证时关键对象的说明如下表:
对象及意思 | 说明 |
---|---|
Subject:主体 | 访问系统的用户,主题可以是用户、程序等,进行认证的都称为主体 |
Principal:身份信息 | 是主体(Subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal) |
crendential:凭证信息 | 只有主体自己知道的安全信息,如密码、证书等 |
1.3 授权
概念:即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
授权(可简单理解为 who 对 what(which) 进行 how 操作)时关键对象说明如下:
对象 | 说明 |
---|---|
Who | 即主体(Subject),主体需要访问系统的资源 |
What | 即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息的等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为A的商品为资源实例,编号为001的商品也属于资源实例 |
How | 即权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001的用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。权限分为粗颗粒和细颗粒,粗颗粒权限是指对资源类型的权限,细颗粒权限是指对资源实例的权限。 |
1.4 权限模型
对于上面的主体、资源、权限通过数据模型表示。
- 主体(账号、密码)
- 资源(资源名称、访问地址)
- 权限(权限名称、资源id)
- 角色(角色名称)
- 角色和权限关系(角色id、权限id)
- 主体和角色关系(主体id 、角色id )
通常企业开发中将资源表和权限表合并为一张权限表,如下:
资源(资源名称、访问地址)
权限(权限名称、资源id)
合并为:
权限(权限名称、资源名称、资源访问地址
1.5 权限分配
对主体分配权限,主体只允许在权限范围内对资源进行操作,比如:对user用户分配商品修改权限,user用户只能对商品进行修改。
权限分配的数据通常需要持久化,根据上面的数据模型创建表并将用户的权限信息存储在数据库中。
1.6 权限控制
用户拥有了权限即可操作权限范围内的资源,系统不知道主体是否具有访问权限,需要对用户的访问进行控制。
1.7 基于角色的访问控制
RBAC基于角色的访问控制(Role-Based-Access-Control)
是以角色为中心进行访问控制,比如:主体的角色为总经理可以查询企业运营报表、查询员工工资信息等。缺点:以角色进行访问控制粒度较粗,如果上面查询工资所需要的角色变化为总经理和部门经理,此时需要修改判断逻辑为“判断主体的橘色是否是总经理或部门经理”,系统可扩展性差。
1.8 基于资源的访问控制
RBAC基于资源的访问控制(Resource Based Access Contaol )是以资源为中心进行访问控制,比如:比如主体必须具有查询工资权限才可以查询员工工资信息等。
优点:系统设计时定义好查询工资的权限标识,即查询工资所需要的角色变化为总经理和部门经理也只需要将“查询工资信息权限”添加到“部门经理角色”的权限列表中,判断逻辑不用修改,系统可扩展性强。
1.9 权限管理解决方案
粗粒度和细粒度
对资源类型的管理称为粗粒度权限管理,即只控制到菜单、按钮、方法,粗粒度的例子比如:用户具有用户管理的权限,具有导出订单明细的权限。
对资源实例的控制称为细粒度权限管理,即控制到数据级别的权限,比如:用户只允许修改本部门的员工信息,用户只允许导出自己创建的订单信息。
如何实现粗粒度和细粒度?
<1>对于粗粒度的权限管理可以很容易做系统架构级别的功能,即系统功能操作使用统一的粗粒度的权限管理。
<2>对于细粒度的权限管理不建议做成系统架构级别的功能,因为对数据级别的控制是系统的业务需求,随着业务需求的变更业务空间变化的空能行很大,建议对数据级别的权限控制在业务层个性化开发,比如:用户只允许修改自己创建的商品信息可以在 Service 接口添加校验实现,service 接口需要传入当前操作让人的标识,与商品信息创建人标识对比,不一致则不允许修改商品信息。
1.10 基于 url 拦截
基于 url 拦截是企业中常见的权限管理方法,实现思路是:将系统操作的每个 url 配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能
2.再谈 Shrio框架
2.1 什么是 Shrio?
Shrio 是 apache 旗下的一个开源框架,它将软件系统中与安全认证相关的功能抽取出来,实现用户身份认证、权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。Shrio 安全框架使用广泛,它可以运行在 web 引用、非 web 应用、集群分布式应用中。
java 中的 spring security(原名Acegi) 也是一个开源的权限管理框架,但是 spring security 依赖 spring 运行,而 Shrio 相对独立。Shrio 使用简单、灵活,所以现在越来越多的用户选择用 Shrio.
2.2 Shrio 核心组件
-
Subject
Subject 即主体
,外部引用于 Subject 进行交互,Subject 记录了当前操作用户,将用户的概念理解为当前操作的主体, 可能是一个通过浏览器请求的用户,也可能是一个运行的程序。
Subject 在 Shrio 中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过 Sbject 进行认证授权,
而 Sbject 是通过 SecurityManager 安全管理器进行认证授权的。 -
SecurityManager
SecurityManager 即安全管理器
,对全部的 Sbject 进行安全管理,它是 Shrio 的核心,通过 SecurityManager 可以完成全部 Sbject 的认证、授权等。实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理等。
SecurityManager是一个接口,继承了 Authenticator ,Authorizer ,SessionManager 这三个接口。 -
Authenticator
Authenticator即认证器
,对用户身份进行认证,Authenticator 是一个接口,Shrio 提供了 ModularRealmAuthenticator实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器。 -
Authorizer
Authorizer即授权器
,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。 -
Realm
Realm即领域
,相当于 datasource 数据源,SecurityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 Realm 就需要从数据库获取用户身份信息。注意
:不要把 Realm 理解成只是从数据源取数据,在 Realm 中还有认证授权校验的相关代码。 -
SessionManager
SessionManager 即会话管理
,Shrio 框架定义了一套会话管理,它不依赖 web 容器的 Session ,所以Shrio 可以使用在非 Web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。 -
SessionDAO
SessionDAO 即会话DAO
,是对 Session 会话操作的一套接口,比如要将 Session 存储到数据库,可以通过 jdbc 将会话存储到数据库。 -
CacheManager
CacheManager 即缓存管理
,将用户权限数据存储在缓存,这样可以提高性能。 -
Cryptografy
Cryptografy即密码管理
,Shrio 提供了一套解密/加密的组件,方便开发。比如提供常用的散列、加/解密功能。
3.认证执行流程
- 创建 token 令牌,token 中有用户提交的认证信息即账号和密码
- 执行 subject.login(token),最终由 SecurityManager 通过 Authentication 进行认证。
- Authentication 的实现。ModularRealmAuthentication 调用 Realm 从 ini 配置文件取用户真实的账号和密码,这里使用的是iniRealm(Shrio 自带的,也是从 ini配置文件中读取)
- iniRealm 先根据 token 中的账号去 ini 中找该账号,如果找不到则给 ModularRealmAuthentication 返回 null,如果找到则匹配密码,密码匹配成功则认证通过。
4.自定义 Realm
由于大部分情况下不是从配置文件 ini 中读取用户信息,而是从数据库中读取用户信息,而此时就需要自定义 Realm .最基础的是 Realm 接口,CacheRealm 负责缓存处理,AuthenticationRealm 负责认证,AuthorizingRealm 负责授权,通常自定义 Realm 继承 AuthorizingRealm .
public class CustomRealm extends AuthorizingRealm{
@Override
public String getName(){
return "customRealm";
}
//支持UsernamePasswordToken
@Override
public boolean supports(AuthenticationToken token){
return token instanceof UsernamePasswordToken;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken toke) throws AuthenticationException{
//从 token 中获取用户身份信息
String username=(String) token.getPrincipal();
//拿 username 从数据库中查询
//...
//如果查询不到则返回 null
if(!username.equals("zhang")){//这里模拟查询不到
return null;
}
//获取从数据库中查询出来的密码
String password = “123” //这里使用静态数据模拟
o
//返回认证信息由父类 AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,password,getName());
return simpleAuthenticationInfo;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){
return null;
}
}
}