《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.im/post/6850037267554287629 「PioneerYi」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

一个系统,如果没有安全控制,是十分危险的,一般安全控制包括身份认证和权限管理。用户访问时,首先需要查看此用户是否是合法用户,然后检查此用户可以对那些资源进行何种操作,最终做到安全访问。身份认证的方式有很多种,最简单的就是直接用户名密码,还有业内比较通用的方式CAS方式登陆等;授权的框架也很多,比如OAuth2,Shiro等。本文首先会讲解一下CAS的概念,以及基于角色的权限管理模型(RBAC)的概念,接着进行数据表的设计,最后讲解如何利用Shiro进行权限管理。

一、CAS身份认证

集中式认证服务(英语:Central Authentication Service,缩写CAS)是一种针对万维网的单点登录协议。它的目的是允许一个用户访问多个应用程序,而只需提供一次凭证。

1.1、名词概念

CAS的核心就是其Ticket,及其在Ticket之上的一系列处理操作。CAS的主要票据有TGT、ST、PGT、PGTIOU、PT,其中TGT、ST是CAS1.0(基础模式)协议中就有的票据,PGT、PGTIOU、PT是CAS2.0(代理模式)协议中有的票据。这些票据谁生成得了?肯定是有相关服务的,主要服务有:KDC,AS,TGS。两者媒介肯定也是有的:TGC。

1.1.1、CAS的Ticket

1)TGT(Ticket Granting Tieckt)

TGT是CAS(具体为 KDC 的 AS 发放)为用户签发的登录票据,拥有了TGT,用户就可以证明自己在CAS成功登录过。TGT封装了Cookie值以及此Cookie值对应的用户信息。用户在CAS认证成功后,CAS生成cookie,写入浏览器,同时生成一个TGT对象,放入自己的缓存,TGT对象的ID就是cookie的值。当HTTP再次请求到来时,如果传过来的有CAS生成的cookie,则CAS以此cookie值为key查询缓存中有无TGT ,如果有的话,则说明用户之前登录过,如果没有,则用户需要重新登录。

简而言之,即获取这样一张票据后,以后申请各种其他服务票据 (ST) 便不必再向 KDC 提交身份认证信息 ( 准确术语是 Credentials) 。

2)ST(Service ticket)

ST是CAS(由 KDC 的 TGS 发放)为用户签发的访问某一service的票据。

用户访问service时,service发现用户没有ST,则要求用户去CAS获取ST。用户向CAS发出获取ST的请求,如果用户的请求中包含cookie,则CAS会以此cookie值为key查询缓存中有无TGT,如果存在TGT,则用此TGT签发一个ST,返回给用户。用户凭借ST去访问service,service拿ST去CAS验证,验证通过后,允许用户访问资源。

任何一台 Workstation 都需要拥有一张有效的 Service Ticket 才能访问域内部的应用 (Applications) 。 如果能正确接收 Service Ticket ,说明在 CASClient-CASServer 之间的信任关系已经被正确建立起来。

3)PGT(Proxy Granting Ticket)

Proxy Service的代理凭据。用户通过CAS成功登录某一Proxy Service后,CAS生成一个PGT对象,缓存在CAS本地,同时将PGT的值(一个UUID字符串)回传给Proxy Service,并保存在Proxy Service里。Proxy Service拿到PGT后,就可以为Target Service(back-end service)做代理,为其申请PT。

4)PT(Proxy Ticket)

PT是用户访问Target Service(back-end service)的票据。如果用户访问的是一个Web应用,则Web应用会要求浏览器提供ST,浏览器就会用cookie去CAS获取一个ST,然后就可以访问这个Web应用了。如果用户访问的不是一个Web应用,而是一个C/S结构的应用,因为C/S结构的应用得不到cookie,所以用户不能自己去CAS获取ST,而是通过访问proxy service的接口,凭借proxy service的PGT去获取一个PT,然后才能访问到此应用。

TGT、ST、PGT、PT之间关系的总结

1:ST是TGT签发的。用户在CAS上认证成功后,CAS生成TGT,用TGT签发一个ST,ST的ticketGrantingTicket属性值是TGT对象,然后把ST的值redirect到客户应用。

2:PGT是ST签发的。用户凭借ST去访问Proxy service,Proxy service去CAS验证ST(同时传递PgtUrl参数给CAS),如果ST验证成功,则CAS用ST签发一个PGT,PGT对象里的ticketGrantingTicket是签发ST的TGT对象。

3:PT是PGT签发的。Proxy service代理back-end service去CAS获取PT的时候,CAS根据传来的pgt参数,获取到PGT对象,然后调用其grantServiceTicket方法,生成一个PT对象。

1.1.2、CAS的服务

CAS的主要服务有:

  • KDC(Key Distribution Center );
  • AS(Authentication Service)它索取 Crendential,发放 TGT;
  • TGS(Ticket Granting Service),索取 TGT ,发放 ST。

1.1.3、CAS的媒介

TGC(Ticket Granting Cookie)

存放用户身份认证凭证的cookie,在浏览器和CAS Server间通讯时使用,并且只能基于安全通道传输(Https),是CAS Server用来明确用户身份的凭证。

1.2、CAS工作原理

CAS的单点登录的认证过程,所用应用服务器受到应用请求后,检查ST和TGT,如果没有或不对,转到CAS认证服务器登录页面,通过安全认证后得到ST和TGT,再重新定向到相关应用服务器,在回话生命周期之内如果再定向到别的应用,将出示ST和TGT进行认证,注意,取得TGT的过程是通过SSL安全协议的。

从网上找了一个比较专业又比较详细的CAS工作原理流程图:

CAS流程

专业版可能比较晦涩难懂,来个通俗版。通俗形象地说就是: 相当于用户要去游乐场,首先要在门口检查用户的身份 ( 即 CHECK 用户的 ID 和 PASS), 如果用户通过验证,游乐场的门卫 (AS) 即提供给用户一张门卡 (TGT)。

这张卡片的用处就是告诉游乐场的各个场所,用户是通过正门进来,而不是后门偷爬进来的,并且也是获取进入场所一把钥匙。

现在用户有张卡,但是这对用户来不重要,因为用户来游乐场不是为了拿这张卡的而是为了游览游乐项目,这时用户摩天楼,并想游玩。

这时摩天轮的服务员 (client) 拦下用户,向用户要求摩天轮的 (ST) 票据,用户说用户只有一个门卡 (TGT), 那用户只要把 TGT 放在一旁的票据授权机 (TGS) 上刷一下。

票据授权机 (TGS) 就根据用户现在所在的摩天轮,给用户一张摩天轮的票据 (ST), 这样用户有了摩天轮的票据,现在用户可以畅通无阻的进入摩天轮里游玩了。

当然如果用户玩完摩天轮后,想去游乐园的咖啡厅休息下,那用户一样只要带着那张门卡 (TGT). 到相应的咖啡厅的票据授权机 (TGS) 刷一下,得到咖啡厅的票据 (ST) 就可以进入咖啡厅

当用户离开游乐场后,想用这张 TGT 去刷打的回家的费用,对不起,用户的 TGT 已经过期了,在用户离开游乐场那刻开始,用户的 TGT 就已经销毁了 。

二、基于角色的权限管理模型

在业界接受度较高的权限模型是RBAC(Role-Based Access Control),基本的概念是将“角色”这个概念赋予用户,在系统中用户通过分配角色从而获得相应的权限,一个用户可以有多个角色,一个角色可以有多个权限,从而实现权限的灵活配置。

2.1、基本的RBAC模型

最基本的RBAC模型,就是由“用户”,“角色”以及“权限”这三个主体组成,一个用户可以有多个角色,一个角色可以有多个权限,他们之间的关系可以是多对一关系,也可以是多对多关系。

用户角色权限关系

2.2、引入用户组的RBAC模型

如果用户数量比较庞大,新增一个角色时,需要为大量用户都重新分配一遍新的角色,工程量巨大,此时可以引入用户组的概念。如果部分用户的使用场景是相对一致和基础的,可以把这些用户打包成一个组,基于这个组的对象进行角色和权限的赋予。最终用户拥有的所有权限 = 用户个人拥有的权限+该用户所在用户组拥有的权限。

2.3、角色分级的RBAC模型

在一些业务场景中,上层角色需要继承下层角色的全部权限,此时则需要使用角色继承的RBAC模型。此时除了对角色进行定义,还需要管理角色间的关系,通过关系来体现角色的层级关系,从而达到继承权限的效果。角色的继承关系主有两种:树形图和有向无环图。

角色继承模式

继承关系常常来源于公司团队的组织架构,此时常常将角色与组织结构进行关联达到继承角色模型的效果。

2.4、角色限制的RBAC模型

在一些产品或系统中,部分角色可能是需要隔离的、不允许被同时赋予一个人的,比如不能既是运动员又是裁判员。因此,有些角色存在互拆关系。此外,限制还可能是数量上的,比如某个产品组中有且只有一个管理员,不允许删除或再分配其他管理员。

根据不同的业务需求,限制的形式很多,需要注意的是不能仅仅依赖后段限制,而是要在前端展示清晰的规则和恰当的限制,避免用户出错。

2.5、权限管理的基本元素

权限管理的基本元素为:用户,角色,资源,操作,权限。

1、用户 应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

2、用户组(可选) 为了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、权限信息。

3、角色 为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

4、资源 权限管理的一个单元实体对象,我们广义的称之为资源,可以是一个人,也可以是一个产品,一个文件,一个页面 等等。 5、操作 对资源进行的实际操作,比如读、写、编辑等等。

6、权限 资源+操作,构成一个权限控制点。

对象间的关系包括:

  • 是否关系
  • 继承关系
  • 限制关系(互斥、范围限制、边界限制、字段限制)

三、数据表设计

按照RBAC模型,数据库可以这样设计:

1、产品表(t_product_info)

字段名称 字段 类型 备注
产品Id pro_id int(11) 自增
产品名称(英) name_en varchar(50) not null
产品名层(中) name_ch varchar(50) not null
创建人 creator varchar(50) not null
所属人 owner varchar(50) not null
描述 description varchar(255)
创建时间 create_time timestamp not null

2、产品成员表(t_product_member)

字段名称 字段 类型 备注
记录ID id int(11) 自增
产品ID pro_id int(11) fk:t_produck_info
成员ID member_id int(11) not null
创建时间 create_time timestamp not null

3、用户信息表(t_user_info)

字段名称 字段 类型 备注
用户ID user_id int(11) not null
英文名 nike_name varchar(10) not null
中文名 real_name varchar(10) not null
创建时间 create_time timestamp not null
更新时间 update_time timestamp not null

4、用户角色表(t_user_role)

字段名称 字段 类型 备注
记录ID id int(11) 自增
用户ID user_id int(11) not null
角色ID role_id varchar(50) not null
创建时间 create_time timestamp not null

5、角色表(t_role)

字段名称 字段 类型 备注
记录ID id int(11) 自增
角色ID role_id varchar(50) not null,比如:A~USER
角色名称 role_name varchar(50) not null
对象 object varchar(50) not null
对象ID object_id varchar(50) not null
角色备注 comment varchar(255)
创建时间 create_time timestamp not null

6、基础角色表(t_role_base)

字段名称 字段 类型 备注
记录ID id int(11) 自增
角色ID role_id varchar(50) not null,比如:A~USER
角色名称 role_name varchar(50) not null
角色备注 comment varchar(255)
权限 permission varchar(255) not null
创建时间 create_time timestamp not null

7、角色权限表(t_role_permission)

字段名称 字段 类型 备注
记录ID id int(11) 自增
角色ID role_id varchar(50) fk:t_role->role_id
权限 permission varchar(255) not null
基础角色ID role_base_id varchar(50) fk:t_role_base->role_id
创建时间 create_time timestamp not null

8、用户组表(t_user_group,可选)

字段名称 字段 类型 备注
组ID id int(11) 自增
组名称 group_name varchar(50) not null
组描述 group_desc varchar(255) not null
创建时间 create_time timestamp not null

9、组角色表(t_user_group_role,可选)

字段名称 字段 类型 备注
记录ID id int(11) 自增
组ID role_id varchar(50) not null
角色ID role_id varchar(255) not null
创建时间 create_time timestamp not null

10、用户权限表(t_user_permission,可选)

字段名称 字段 类型 备注
记录ID id int(11) 自增
用户ID role_id varchar(50) not null
权限 permission varchar(255) not null
创建时间 create_time timestamp not null

四、角色及权限点设计

权限控制的整个过程可以描述为:“谁”对“什么”进行什么”操作",从而,引出我们需要做的工作有:角色设计,资源定义,以及对资源的操作定义。再详细描述下,鉴权就是根据用户身份(角色)获得其对那些资源,可以进行什么操作,其中对资源的操作做为一个独立的权限体。

4.1、定义系统中的用户角色

一般是采用“通用角色+实例角色”的模式,实例角色可继承通用角色,从而拥有通用角色的权限。

常见的通用角色定义:ADMIN、MANAGER、MEMBER、GUEST 常见角色权限分配: 1)SUPER_ADMIN,具有系统一切权限 1)产品ADMIN,具有当前产品所有权限; 2)产品MANAGER,不具备删除权限,可修改,添加成员等 3)产品MEMEBER,可查看,修改信息,不可添加成员; 4)产品GUEST,只可查看

实例角色: 实例角色一般可以这样定义:“资源点+通用角色+资源ID”

注:其中资源可能是产品,可能是页面,也可能是菜单等

4.2、定义系统中的资源以及操作

一般系统中的最常见资源就是:产品(P) 一般对资源的主要操作包括: 增加(CREATE)、删除(DELETE)、修改(EDIT)、查看(VIEW)

当然,系统中的资源肯定不止产品,同时产品这个粒度有些太粗,还可以更细化控制,当然一切都根据实际业务需求情况定义相应的资源点和操作。

4.3、权限体策略

权限控制策略采用五元组,如下:

资源:操作:实例:BU:密级

其中,资源可以是人,也可以是一个需求,一个文件等等;操作为对资源的操作;实例极为产品ID或者用户ID;BU,密级用于控制不同BU,不同保密模块的可见性

五、身份认证加权限管理实施

JAVA可以采用SHIRO框架,一个最简单的一个Shiro应用: 1)应用代码通过subject授权,而subject又委托给SecurityManager; 2)我们需要给Shiro的security注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断;

Shiro的最主要要做的工作其实就是两个:身份认证和权限校验,下面分别进行介绍。

5.1、身份认证

通过前面的文章分析,我们知道自定义身份校验校验逻辑,只需要继承AuthenticatingRealm即可,Override如下接口进行身份认证:

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){}

上面这个接口,参数AuthenticationToken,它可以自定义子类实现,框架提供的有:UsernamePasswordToken,CasToken。

如果是采用用户名密码方式登陆,那么就构造一个UsernamePasswordToken,然后取数据查询用户名密码是否有效;如果是采用的CAS方式登陆,那么就通过ticket构造一个CasToken,然后与CAS服务交互验证ticket是否有效。如果验证通过会生成一个AuthenticationInfo。 此时身份认证完成。

最简单的用户密码登陆身份校验代码 CAS方式验证首先得有CAS系统,这里就不说明CAS方式怎么验证了,说一下怎么用用户密码登陆进行身份校验,认证流程都一样

自定义一个AuthenticatingRealm:

public class MyRealm1 implements AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal(); //得到用户名
String password = new String((char[])token.getCredentials()); //得到密码
//取数据库中看用户名是否有效
//checkUserInfo();

//如果身份认证验证成功,返回一个AuthenticationInfo实现;
return new SimpleAuthenticationInfo(username, password, getName());
}
}

5.2、权限校验

权限校验主要要做的事情就是完成"从数据库中查出用户所拥有的所有权限是否包含当前待校验的权限"这么一个判断过程,因此主要要做的就是:1)从数据库中查出用户所拥有的所有权限;2)解析权限,看看是否包含待校验的权限。

1、第一步:获取用户权限信息 获取用户权限信息这个过程是在Realm中完成的,继承AuthorizingRealm,然后Override如下两个接口获取用户的权限信息:

//获取用户身份信息,Authorization前需要先获取用户身份信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){}

//获取用户权限信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){
}

权限信息查询过程一般为: 1)从数据库中读区用户自身所配权限; 2)从数据库中读取用户角色所用拥有的权限(角色包含实例角色和BASE角色) 3)用户最终的权限:用户自身权限+用户角色权限

2、第二步:权限校验

1)如果通过角色校验,则调用hasRole,与传入的角色比较即可;

2)如果通过权限体校验,则调用isPermitted(...),与传入的权限进行比较即可。

shiro内部逻辑如下:首先通过PermissionResolver将权限字符串转换成相应的Permission实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;接着调用Permission.implies(Permission p)逐个与传入的权限比较,如果有匹配的则返回true,否则false。

文章目录
  1. 1. 一、CAS身份认证
    1. 1.1. 1.1、名词概念
      1. 1.1.1. 1.1.1、CAS的Ticket
      2. 1.1.2. 1.1.2、CAS的服务
      3. 1.1.3. 1.1.3、CAS的媒介
      4. 1.1.4. 1.2、CAS工作原理
  2. 2. 二、基于角色的权限管理模型
    1. 2.1. 2.1、基本的RBAC模型
    2. 2.2. 2.2、引入用户组的RBAC模型
    3. 2.3. 2.3、角色分级的RBAC模型
    4. 2.4. 2.4、角色限制的RBAC模型
    5. 2.5. 2.5、权限管理的基本元素
  3. 3. 三、数据表设计
    1. 3.0.1. 1、产品表(t_product_info)
    2. 3.0.2. 2、产品成员表(t_product_member)
    3. 3.0.3. 3、用户信息表(t_user_info)
    4. 3.0.4. 4、用户角色表(t_user_role)
    5. 3.0.5. 5、角色表(t_role)
    6. 3.0.6. 6、基础角色表(t_role_base)
    7. 3.0.7. 7、角色权限表(t_role_permission)
    8. 3.0.8. 8、用户组表(t_user_group,可选)
    9. 3.0.9. 9、组角色表(t_user_group_role,可选)
    10. 3.0.10. 10、用户权限表(t_user_permission,可选)
  • 4. 四、角色及权限点设计
    1. 4.0.1. 4.1、定义系统中的用户角色
    2. 4.0.2. 4.2、定义系统中的资源以及操作
    3. 4.0.3. 4.3、权限体策略
  • 5. 五、身份认证加权限管理实施
    1. 5.1. 5.1、身份认证
    2. 5.2. 5.2、权限校验