目录
3.4.1、两大鉴权框架 shiro 和 spring security
4、Shiro 中有四大功能——身份验证,授权,会话管理和加密。
一、基本概念
1.1、什么是认证
认证 :用户认证就是判断一个用户的身份是否合法的过程
通俗来讲,用户输入账户密码登录的过程就叫认证。
系统为什么要认证?
认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。
1.2 、什么是会话
可以理解为客户端和服务器之间的一次私密谈话,在一次对话中可能会有多个请求和响应;同时要具有鉴别多个请求是来自同一个客户端的一次对话的功能;
用户认证通过后,为了避免用户的每次操作都进行认证,可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于session方式、基于token方式等。
基于session的认证方式如下图:
它的交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话)中,发给客户端的sesssion_id 存放到 cookie 中,这样用户客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户的合法校验,当用户退出系统或session过期销毁时,客户端的session_id也就无效了。
基于token方式如下图:
它的交互流程是,用户认证成功后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份。
基于session的认证方式由Servlet规范定制,服务端要存储session信息需要占用内存资源,客户端需要支持cookie;基于token的方式则一般不需要服务端存储token,并且不限制客户端的存储方式,但同时也了可能需要支持cookie,也可以利用localStorage来存储;如今移动互联网时代更多类型的客户端需要接入系统,系统多是采用前后端分离的架构进行实现,所以基于token的方式更适合。
1.3、授权的数据模型
主体 object(用户)—— 权限 permission(增删改查)—— 资源 resource(服务模块、功能)
主体、资源、权限相关的数据模型如下:
主体(用户id、账号、密码、...)
资源(资源id、资源名称、访问地址、...)
权限(权限id、权限标识、权限名称、资源id、...)
角色(角色id、角色名称、...)
角色和权限关系(角色id、权限id、...)
主体(用户)和角色关系(用户id、角色id、...)
主体(用户)、资源、权限关系如下图:
通常企业开发中将资源和权限表合并为一张权限表,如下:
资源(资源id、资源名称、访问地址、...)
权限(权限id、权限标识、权限名称、资源id、...)
合并为:
权限(权限id、权限标识、权限名称、资源名称、资源访问地址、...)
修改后数据模型之间的关系如下图:
1.3.1、权限的分类
权限可以分为功能权限和数据权限,功能权限可以理解就是一个资源模块,或者一个API,数据权限就是该用户能访问某功能API,但是只能访问限定的数据范围。
1.4 RBAC
如何实现授权?业界通常基于RBAC实现授权。
1.4.1 基于角色的权限控制
RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等;
1.4.2 基于资源的权限控制
RBAC基于资源的访问控制(Resource-Based Access Control)是按资源(或权限)进行授权,比如:用户必须具有查询工资权限才可以查询员工工资信息等;
1.4.3、两种方式的优缺点对比
基于角色的权限控制
优点:容易配置,只需要把每个角色的权限在配置好,保存在数据库,所有用户在注册的时候就分配好角色,那么所有的用户的权限可以同一配置了,当角色的权限发生变化的时候,只需要将对应的角色的权限配置好就行了。
缺点:有些用户归属于同一角色,但是因为岗位略有不同,那么需要一些特殊的权限,那么需要子角色,如果一直这样下去,可能有子角色的子角色...可能需要一直创建很多子级角色。概括来说,粒度不够细。
基于资源的权限控制
优点:权限的控制的粒度比较灵活,可以按照不同资源模块来限制用户的权限,比如客户端用户不能访问系统管理,那么不用管系统管理里面有多少功能API,不用细分地给用户分配每一个API的权限,一律屏蔽不允许,也可以允许访某个资源模块中的限定的一些API;
缺点:需要批量修改用户的权限的时候就很麻烦。
综合对比还是基于角色的权限控制实现会更便于控制,虽然同一角色会特殊情况会有不同的功能权限,但是只需要创建子角色就可以解决,这样,即使需要改变某些群体的权限就可以修改角色的权限达到批量修改的目的,但是如果纯粹按照基于资源的权限控制方式设计权限控制,那么当有些用户权限需要变动的时候,不能批量修改。
角色相当于用户和权限的之间的一个中间代理;
二、SSO单点登录
单体、单机项目的权限控制比较简单,因为只限于一个系统中的权限认证和访问权限控制;注意前端和后端接口同时做权限控制就好;至于是会话的实现是用session和token的方式,都是可以的。token更复杂一点,但是不需要在服务端存储session,也不用在cookie存储sessionID,也更安全。
那么在彼此相关的一系列系统之间,比如某个公司的垂直业务拆分体系系统,各个子系统,OA行政,人力资源管理(工作目标、绩效考核)、门户网站,财务汇总,生产管理,仓储管理,物流管理,系统后台等等,在访问每个系统都要去登录认证一遍,从而获取权限是不是很麻烦呢?
还有分布式系统、微服务系统中各个服务之间的权限校验,也是同样的需求;
甚至还有第三方系统需要借助某个系统提供认证,或者某些小一点的平台需要借助第三方大一点的平台(支付宝、微信)来免注册登录认证,都是如此;
2.1、什么是SSO单点登录
SSO(Single Sign On)流行的业务整合解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
- 可以是同一个系统中,比如分布式系统中,跨不同的子系统的访问,不需要每个子系统都需要登录验证一遍,否则会非常麻烦;
- 也可以是不同的应用,但却是可以互相信任,相互关联的一系列应用,比如垂直业务系统中,也要求只需要在一个应用登录后其他的应用站点,不再需要登录就可以跳转访问;
- 还可以是毫无关联的系统(对第三方系统提供认证),但是允许其中一个作为另外一个应用的第三方认证方,比如微信,QQ,支付宝,可以作为很多电商的免注册的第三方认证系统。
什么是分布式系统?什么是垂直业务体系系统?参照分布式服务架构和微服务架构
2.2、单点登录的实现
1、共享session
tomcat支持session共享,但是有广播风暴;session会存储在内存,用户量大的时候,占用资源就严重,不推荐;
这种方案下,通常由以下几种做法:
- session复制。在多台应用服务器之间同步session,并使session保存一致,对外透明。
- session黏贴。当用户访问集群中某台服务器后,强制指定后续所有请求均落到此服务器上。
- session集中存储。将session存入指定的服务器中,所有服务器应用实例都统一从指定服务器中获取session信息。
2、使用redis存储token
- 服务端使用UUID生成随机64位或者128位token,放入redis中,然后返回给客户端并存储在cookie中,或者在本地磁盘存储中,用户每次访问都携带此token,服务端去redis中校验是否有此用户即可(并不是逆向解析token);
- 可以认为是共享session(集中存储)的改进版,因为这种方式和共享session一样需要在服务端存储已登录的信息。但是它拿出一个单独的缓存服务来存储token,不会影响正式的资源服务的服务器的内存资源。所有的服务校验的时候都去访问同一的缓存服务,没有像session共享的时候的session复制带来的资源消耗,当然主备之间也会有主从同步带来的数据通信的资源消耗,但是这也比session每个节点的共享复制要好得多。
- 所以这种方式目前也有很多公司都在使用。但是这种方式不是最优的方案,这种方式需要每次都去redis里拿数据检验一下,在访问量大的时候,每次访问redis也是一种资源(比如网络资源和IO)的消耗,所以现在很多应用已经开始使用我们今天要讲的JWT;
3、客户端存储token(JWT token)
客户端存储JWT token,服务端不用存储token,且token是用户的一些基础信息的加上头部、签名的等信息一起加密后的密文。逆向解密可以得到用户的基础信息,比如用户ID,可以根据用户ID到缓存或者数据中获取用户的权限等。
2.3、三种方案的对比和主流选取
基于session和基于jwt的方式的主要区别就是用户的状态保存的位置,session是保存在服务端的,而jwt是保存在客户端的。
以上三种方式中,除了共享session,需要cookie的支持,后面两种token,可以储存在 Cookie 里面,也可以储存在 localStorage。而存储在cookie里面会有CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
主流还是倾向于JWT来作为token,节省了一次redis中读取数据,验证token是否有效的过程,数据IO和网络传输的资源