点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
关注公众号后台回复pay或mall获取实战项目资料+视频
作者:PioneerYi
juejin.im/post/6850037267554287629
一个系统,如果没有安全控制,是十分危险的,一般安全控制包括身份认证和权限管理。用户访问时,首先需要查看此用户是否是合法用户,然后检查此用户可以对那些资源进行何种操作,最终做到安全访问。身份认证的方式有很多种,最简单的就是直接用户名密码,还有业内比较通用的方式CAS方式登陆等;授权的框架也很多,比如OAuth2,Shiro等。本文首先会讲解一下CAS的概念,以及基于角色的权限管理模型(RBAC)的概念,接着进行数据表的设计,最后讲解如何利用Shiro进行权限管理。
集中式认证服务(英语:Central Authentication Service,缩写CAS)是一种针对万维网的单点登录协议。它的目的是允许一个用户访问多个应用程序,而只需提供一次凭证。
CAS的核心就是其Ticket,及其在Ticket之上的一系列处理操作。CAS的主要票据有TGT、ST、PGT、PGTIOU、PT,其中TGT、ST是CAS1.0(基础模式)协议中就有的票据,PGT、PGTIOU、PT是CAS2.0(代理模式)协议中有的票据。这些票据谁生成得了?肯定是有相关服务的,主要服务有:KDC,AS,TGS。两者媒介肯定也是有的:TGC。
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对象。
CAS的主要服务有:
KDC(Key Distribution Center );
AS(Authentication Service)它索取 Crendential,发放 TGT;
TGS(Ticket Granting Service),索取 TGT ,发放 ST。
TGC(Ticket Granting Cookie)
存放用户身份认证凭证的cookie,在浏览器和CAS Server间通讯时使用,并且只能基于安全通道传输(Https),是CAS Server用来明确用户身份的凭证。
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),基本的概念是将“角色”这个概念赋予用户,在系统中用户通过分配角色从而获得相应的权限,一个用户可以有多个角色,一个角色可以有多个权限,从而实现权限的灵活配置。
最基本的RBAC模型,就是由“用户”,“角色”以及“权限”这三个主体组成,一个用户可以有多个角色,一个角色可以有多个权限,他们之间的关系可以是多对一关系,也可以是多对多关系。
用户角色权限关系如果用户数量比较庞大,新增一个角色时,需要为大量用户都重新分配一遍新的角色,工程量巨大,此时可以引入用户组的概念。如果部分用户的使用场景是相对一致和基础的,可以把这些用户打包成一个组,基于这个组的对象进行角色和权限的赋予。最终用户拥有的所有权限 = 用户个人拥有的权限+该用户所在用户组拥有的权限。
在一些业务场景中,上层角色需要继承下层角色的全部权限,此时则需要使用角色继承的RBAC模型。此时除了对角色进行定义,还需要管理角色间的关系,通过关系来体现角色的层级关系,从而达到继承权限的效果。角色的继承关系主有两种:树形图和有向无环图。
角色继承模式继承关系常常来源于公司团队的组织架构,此时常常将角色与组织结构进行关联达到继承角色模型的效果。
在一些产品或系统中,部分角色可能是需要隔离的、不允许被同时赋予一个人的,比如不能既是运动员又是裁判员。因此,有些角色存在互拆关系。此外,限制还可能是数量上的,比如某个产品组中有且只有一个管理员,不允许删除或再分配其他管理员。
根据不同的业务需求,限制的形式很多,需要注意的是不能仅仅依赖后段限制,而是要在前端展示清晰的规则和恰当的限制,避免用户出错。
权限管理的基本元素为:用户,角色,资源,操作,权限。
1、用户 应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。
2、用户组(可选) 为了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、权限信息。
3、角色 为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。
4、资源 权限管理的一个单元实体对象,我们广义的称之为资源,可以是一个人,也可以是一个产品,一个文件,一个页面 等等。5、操作 对资源进行的实际操作,比如读、写、编辑等等。
6、权限 资源+操作,构成一个权限控制点。
对象间的关系包括:
是否关系
继承关系
限制关系(互斥、范围限制、边界限制、字段限制)
按照RBAC模型,数据库可以这样设计:
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
产品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 |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
记录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 |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
用户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 |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
记录ID | id | int(11) | 自增 |
用户ID | user_id | int(11) | not null |
角色ID | role_id | varchar(50) | not null |
创建时间 | create_time | timestamp | not null |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
记录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 |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
记录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 |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
记录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 |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
组ID | id | int(11) | 自增 |
组名称 | group_name | varchar(50) | not null |
组描述 | group_desc | varchar(255) | not null |
创建时间 | create_time | timestamp | not null |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
记录ID | id | int(11) | 自增 |
组ID | role_id | varchar(50) | not null |
角色ID | role_id | varchar(255) | not null |
创建时间 | create_time | timestamp | not null |
字段名称 | 字段 | 类型 | 备注 |
---|---|---|---|
记录ID | id | int(11) | 自增 |
用户ID | role_id | varchar(50) | not null |
权限 | permission | varchar(255) | not null |
创建时间 | create_time | timestamp | not null |
权限控制的整个过程可以描述为:“谁”对“什么”进行什么”操作",从而,引出我们需要做的工作有:角色设计,资源定义,以及对资源的操作定义。再详细描述下,鉴权就是根据用户身份(角色)获得其对那些资源,可以进行什么操作,其中对资源的操作做为一个独立的权限体。
一般是采用“通用角色+实例角色”的模式,实例角色可继承通用角色,从而拥有通用角色的权限。
常见的通用角色定义:ADMIN、MANAGER、MEMBER、GUEST 常见角色权限分配:1)SUPER_ADMIN,具有系统一切权限 1)产品ADMIN,具有当前产品所有权限;2)产品MANAGER,不具备删除权限,可修改,添加成员等 3)产品MEMEBER,可查看,修改信息,不可添加成员;4)产品GUEST,只可查看
实例角色:实例角色一般可以这样定义:“资源点+通用角色+资源ID”
注:其中资源可能是产品,可能是页面,也可能是菜单等
一般系统中的最常见资源就是:产品(P) 一般对资源的主要操作包括:增加(CREATE)、删除(DELETE)、修改(EDIT)、查看(VIEW)
当然,系统中的资源肯定不止产品,同时产品这个粒度有些太粗,还可以更细化控制,当然一切都根据实际业务需求情况定义相应的资源点和操作。
权限控制策略采用五元组,如下:
资源:操作:实例:BU:密级
其中,资源可以是人,也可以是一个需求,一个文件等等;操作为对资源的操作;实例极为产品ID或者用户ID;BU,密级用于控制不同BU,不同保密模块的可见性
JAVA可以采用SHIRO框架,一个最简单的一个Shiro应用:1)应用代码通过subject授权,而subject又委托给SecurityManager;2)我们需要给Shiro的security注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断;
Shiro的最主要要做的工作其实就是两个:身份认证和权限校验,下面分别进行介绍。
通过前面的文章分析,我们知道自定义身份校验校验逻辑,只需要继承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());
}
}
复制代码
权限校验主要要做的事情就是完成"从数据库中查出用户所拥有的所有权限是否包含当前待校验的权限"这么一个判断过程,因此主要要做的就是: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。
“https://shiro.apache.org/
https://github.com/apache/shiro
https://jinnianshilongnian.iteye.com/blog/2018398
https://my.oschina.net/bochs/blog/2248956
https://blog.itning.top/posts/Essays/20190408-A-brief-explanation-of-single-sign-on-SSO-and-centralized-authentication-service-CAS-
有热门推荐????
从此抛弃理try...catch!
记一次使用 Lombok线上翻车的事故!
Sentinel+Nacos实现资源流控、降级、热点、授权
Mybatis 使用的 9 种设计模式,真是太有用了
Java数组转List的三种方式及对比
推荐一款日志切割神器!我常用~
使用 Stream API 高逼格 优化 Java 代码!
Spring MVC请求处理过程不是两张流程图就能讲清楚的
撸一个简易聊天室,不信你学不会实时消息推送(附源码)
最后分享一套微服务电商项目教程(资料笔记+视频):点击阅读全文获取面试资料+项目实战资料(电商/聚合支付)
SPringCloud微服电商完整务教程
1.框架搭建
- 电商项目介绍
- 微服务环境搭建
- 数据库搭建
2.分布式存储系统
- FastDFS原理讲解
- 文件上传
- 文件下载
3.商品发布
- 表结构梳理
- 代码生成器的使用
- 商品增删改
- 商品查询
4.lua,canal实现广告缓存
- 首页广告表设计
- Lua安装使用讲解
- Nginx限流实战
- Canal安装,原理介绍
- Canal同步数据实现
5.索引搜索
- ES安装讲解
- Kibana安装讲解
- DSL语句
- ES API使用
6.商品搜索
- ES 高级搜索功能
- ES 排序规则
7.Thymeleaf实现静态页面
- Thymeleat 缓存配置讲解
- 搜索页面讲解
8.微服务网关和Jwt令牌
- 微服务网关Zuul/Gateway介绍
- 网关之负载和限流
- 用户服务搭建
- JWT token讲解
- 网关鉴权
9.Spring Security Oauth2
- 单点登陆介绍
- Oauth2介绍
- 共钥私钥讲解
- 加密算法讲解
10.购物车
- 购物车分析和购物车种类分析
- 订单服务创建
- 购物车功能实现
11.订单
- 用户地址测试
- 下单问题分析,幂等
- 用户积分规则
- 二维码生产讲解
- 微信支付流程及模式讲解
12.微信支付
- 微信支付SDK使用讲解
- 微信支付状态查询
- 内网穿透 花生壳
- 微信支付回调
- rabbitMQ 延时队列讲解
13.秒杀基础
- 秒杀需求分析
- 秒杀服务搭建
- 秒杀之Redis
- 秒杀之多线程
14.秒杀核心
- 重复抢单下单问题
- 超卖问题
- 秒杀支付
15.分布式事物
- 分布式事物介绍
- CAP理论介绍
- 2pc/3pc 机制讲解
- TCC事物补偿
- Seata案列讲解
16.高可用集群
- 分布式和集群概念
- Eureka集群介绍
- Redis 集群介绍
- RabbitMq集群安装
点击阅读原文,前往上面微服务电商教程文档