SpringBoot+Shiro+Jwt填坑-1
shiro介绍
RBAC
在了解Shiro之前,我们需要认知用户权限模型。
基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。
为实现一个较为灵活的用户权限数据模型,通常把用户信息单独用一个实体表示,用户权限信息用两个实体表示。
- 用户信息用
LoginAccount表示,最简单的用户信息可能只包含用户名loginName及密码password两个属性。实际应用中可能会包含用户是否被禁用,用户信息是否过期等信息。 - 用户权限信息用
Role与Permission表示,Role与Permission之间构成多对多关系。Permission可以理解为对一个资源的操作,Role可以简单理解为Permission的集合。 - 用户信息与
Role之间构成多对多关系。表示同一个用户可以拥有多个Role,一个Role可以被多个用户所拥有。

(图源:RBAC模型:基于用户-角色-权限控制的一些思考 | 人人都是产品经理)

Shiro
Java常见安全管理框架
一般项目都会有严格的认证和授权操作,在 Java 开发领域常见安全框架有Shiro和Spring Security
Shiro是个轻级的安全管理框架,提供了认证、授权、会话管理、密码管理、缓存管理等功能Spring Security是个相对复杂的安全管理框架,功能比Shiro更加强大,权限控制细粒度更高,对OAuth支持 更友好,又因为Spring Security源自Spring家族,因此可以和Spring框架无缝整合,特别是Spring Boot中提供的自动化配 方案,可以让Spring Security的使用更加便捷

(图源:学习springBoot(11)shiro安全框架 | Harries Blog™)
作用
- 验证用户身份
- 用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限
- 在非
Web或EJB容器的环境下可以任意使用Session API - 可以响应认证、访问控制,或者
Session生命周期中发生的事件 - 可将一个或以上用户安全数据源数据组合成一个复合的用户
view(视图) - 支持单点登录(
SSO)功能 - 支持提供
Remember Me服务,获取用户关联信息而无需登录
…
下图是Shiro的功能模块:

(图源:Apache Shiro | Simple. Java. Security.)
Authentication:认证,有时也简称为“登录”,这是一个证明用户是他们所说的他们是谁的行为Authorization:授权,访问控制的过程,例如“用户是否允许编辑帐户”,“该用户是否允许查看此网页”,“该用户是否可以访问”到这个按钮?“这些都是决定用户有权访问的决定,因此都代表授权检查Cryptography:密码术是通过隐藏信息或将其转换为无意义的信息来保护源信息免受不良访问的做法,因此没有其他人可以阅读它。Shiro专注于密码学的两个核心要素:使用公钥或私钥加密数据的密码,以及对密码等数据进行不可逆转加密的哈希(也称为消息摘要)Session Management:Session会话,会话是您的用户在使用您的应用程序时携带一段时间的数据桶。传统上,会话专用于Web或EJB环境。Shiro支持任何应用程序环境的会话(即使在非Web或EJB应用程序中,也可以管理特定于用户的会话)。此外,Shiro还提供许多其他强大功能来帮助您管理会话Web Support:Shiro的web支持的API能够轻松地帮助保护Web应用程序。主要就是用来对Web程序进行一个好的支持的Caching:确保安全操作快速而又高效Concurrency:利用它的并发特性来支持多线程应用程序Testing:测试支持的存在来帮助你编写单元测试和集成测试,并确保你的能够如预期的一样安全"Run As":其实这个就是有是有允许一个用户假设为另外一个用户身份的功能,有时候在管理脚本的时候很有效果Remember Me:在会话中记住用户的身份
核心
三大核心组件,Subject、SecurityManager和Realms。
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。可以理解成控制中心,所有请求最终基本上都通过它来代理转发,一般我们程序中不需要直接跟他打交道。外观模式(
Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 外观模式又称为门面模式,它是一种对象结构型模式。Realm:Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和或授权。配置多个Realm是可以的,但是至少需要一个。
认证与授权处理过程
- 被
Shiro保护的资源,才会经过认证与授权过程 - 用户访问受
Shiro保护的URL Shiro首先检查用户是否已经通过认证,如果未通过认证检查,则跳转到登录页面,否则进行授权检查。认证过程需要通过Realm来获取用户及密码信息,通常情况我们实现JDBC Realm,此时用户认证所需要的信息从数据库获取。如果使用了缓存,除第一次外用户信息从缓存获取。- 认证通过后接受
Shiro授权检查,授权检查同样需要通过Realm获取用户权限信息。Shiro需要的用户权限信息包括Role或Permission,可以是其中任何一种或同时两者,具体取决于受保护资源的配置。如果用户权限信息未包含Shiro需要的Role或Permission,授权不通过。只有授权通过,才可以访问受保护URL对应的资源,否则跳转到“未经授权页面”。

(图源:Shiro简介 · 疯狂的小鸡)
Shiro的Filter链
Shiro的认证和授权都离不开Filter,因此需要对Shiro的Filter的运行流程很清楚,才能自定义Filter来满足企业的实际需要。另外Shiro的Filter虽然原理都和Servlet的Filter相似,甚至都最终继承相同的接口,但是实际还是有些差别。Shiro中的Filter主要是在ShiroFilter内,对指定匹配的URL进行拦截处理,它有自己的Filter链;而Servlet的Filter和ShiroFilter是同一个级别的,即先走Shiro自己的Filter体系,然后才会委托给Servlet容器的FilterChain进行Servlet容器级别的Filter链执行
与SpringBoot集成(没有jwt)
依赖
shiro-spring
1 | <dependency> |
spring-boot-starter-web
1 | <dependency> |
其他
其他自己需要的依赖可以自行补全
RBAC思想建表
这里使用Spring Data JPA来处理持久层
按照RBAC思想,最简单的得有用户信息,角色信息,权限信息
用户信息
1 |
|
角色信息
1 |
|
权限信息
1 |
|
根据以上的代码会自动生成 user_info(用户信息表)、sys_role(角色表)、sys_permission(权限表)、sys_user_role(用户角色表)、sys_role_permission(角色权限表)这五张表
测试数据如下:
1 | INSERT INTO `user_info` (`uid`,`username`,`name`,`password`,`salt`,`state`) VALUES ('1', 'admin', '管理员', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', 0); |
Shiro配置
ShiroConfig
首先要配置的是 ShiroConfig 类,Apache Shiro 核心通过 Filter来实现,就好像 SpringMvc 通过 DispachServlet 来主控制一样。
既然是使用 Filter 一般也就能猜到,是通过 URL 规则来进行过滤和权限校验,所以我们需要定义一系列关于 URL 的规则和访问权限。
1 |
|
Filter Chain 定义说明:
- 一个
URL可以配置多个Filter,使用逗号分隔 - 当设置多个过滤器时,全部验证通过,才视为通过
- 部分过滤器可指定参数,如
perms,roles
登录认证实现
在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在 Shiro 中,最终是通过 Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在 Realm 中会直接从我们的数据源中获取 Shiro 需要的验证信息。可以说,Realm 是专用于安全框架的 DAO.Shiro 的认证过程最终会交由 Realm 执行,这时会调用 Realm 的getAuthenticationInfo(token)方法。
该方法主要执行以下操作:
检查提交的进行认证的令牌信息
根据令牌信息从数据源(通常为数据库)中获取用户信息
对用户信息进行匹配验证。
验证通过将返回一个封装了用户信息的
AuthenticationInfo实例。验证失败则抛出
AuthenticationException异常信息。
而在我们的应用程序中要做的就是自定义一个 Realm 类,继承AuthorizingRealm 抽象类,重写doGetAuthenticationInfo()
1 | /** |
链接权限的实现
Shiro的权限授权是通过继承AuthorizingRealm抽象类,重载doGetAuthorizationInfo();当访问到页面的时候,链接配置了相应的权限或者 Shiro 标签才会执行此方法否则不会执行,所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回 null 即可。在这个方法中主要是使用类SimpleAuthorizationInfo进行角色的添加和权限的添加。
1 |
|
当然也可以添加 set 集合:roles 是从数据库查询的当前用户的角色,stringPermissions 是从数据库查询的当前用户对应的权限
1 | authorizationInfo.setRoles(roles); |
如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “perms[权限添加]”);就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问;
如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “roles[100002],perms[权限添加]”);就说明访问/add这个链接必须要有“权限添加”这个权限和具有“100002”这个角色才可以访问
登录实现
登录过程其实只是处理异常的相关信息,具体的登录验证交给 Shiro 来处理
1 |
|
参考:
学习springBoot(11)shiro安全框架 | Harries Blog™
Spring Boot (十四): Spring Boot 整合 Shiro-登录认证和权限管理 - 纯洁的微笑 - 博客园