Shiro
- 权限管理框架包含: 认证 和 授权。
认证
-
各种登录方式统称为认证,包含(用户名密码,门禁卡,指纹,刷脸等等)
主体
- 用户信息,列如账户信息
身份信息
- 主体进行认证登录时的表示,必须唯一。列如用户名,身份证号,人脸…
凭证信息
- 只有主体知道的安全信息,密码,证书…等
授权
-
访问控制,主体信息通过认证后分配给哪些权限可以访问。
-
将系统中的资源分配给用户主体的过程。用户每次访问系统都进行拦截认证–>授权检查。
资源
- 系统中提供可访问的菜单或功能服务。
权限
- 一个用户主体可能访问哪些资源,分配的权限。
权限管理的解决方案
URL拦截器自定义判断
-
通过springmvc的AOP进行权限判断,自己手动封装。不常用
-
web服务的权限拦截通过 URL请求路径进行判断拦截。
-
权限框架Shiro
- 节省系统开发时间,框架提供了完善的 认证 和 授权 的相关操作。
- 例如: Shiro 和 springSecurity
Shiro简介
- Shiro是
apache
下一个开源安全管理框架。 - 将安全认证相关功能提取封装,实现用户身份认证,权限授权,加密,会话管理等功能。
- Shiro可以运行在web应用和非web应用,集群分布式应用中也同样适用。
Subject主体
Subject
是 shiro 的一个接口,接口中定义了很多认证授权相关的方法,Subkect通过SecurityManager
安全管理器进行认证授权。
SecurityManager安全管理器-主要核心服务
SecurityManager
:安全管理器,是 shiro 的核心。- 负责对所有的
Subject
进行安全管理。完成对Subject
的认证 , 授权 等操作。 SecurityManager
- 通过
Authenticator
进行认证。 - 通过
Authorize
进行授权。 - 通过
SessionManager
进行会话管理等。
- 通过
SecurityManager
是一个接口,继承了Authenticator
,Authorize
,SessionManager
三个接口。
Authenticator 认证器
- 对主体
Subject
进行认证登录操作。
Authorize 授权器
- 用户通过 认证器后,在访问功能/菜单/接口时,判断用户是否此功能的操作权限。
Realm 权限存储
SecurityManager
进行安全认证需要通过Realm
获取用户的权限数据。Realm
从数据库存储中获取到用户的权限信息Realm
也是一个接口,有多种实现。- 将用户的权限信息写在配置文件
ini配置文件
- 将用户的全选信息存储到数据库(
RBAC模式
)
- 将用户的权限信息写在配置文件
- 就是配置shiro用户权限的信息数据接口
SessionManger会话管理
SessionManager
会话管理器。- shiro 自己定义的一套会话管理,并非web服务的session,并不依赖web服务容器session。
shiro
可以使用在非web应用上,可以将分布式应用的会话集中在一点管理,可实现单点登录。
SessionDao会话操作
- 对shiro 会话操作的一套接口,可以将用户的会话信息进行自定义扩展操作,比如存储到redis中。
CacheManager缓存管理
- 用于对shiro中的session、realm中的认证信息、授权信息进行缓存 ,提供系统性能。
Cryptography密码管理器
- shrio提供了一套加密/解密的组件,进行明文密码的加密操作,进行散列算法的加密操作,存储在数据库中。
Shiro基本格式
maven依赖
-
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency>
语法格式
-
不集成spring则需要在
resource
下创建一个shiro.ini
的配置文件,进行认证和授权的信息配置。 -
# users代表用户信息, zhangsan-账号, 123456-密码, admin-角色 [users] zhangsan=123456,admin lisi=456789,public # 角色资源分配 product-菜单功能 view-查询 ****等操作权限 [roles] admin=product:view,product:create,product:update,product:delete
简单代码示例
-
通过
shiro
的SecurityManager
安全管理器进行 ini 认证授权信息文件的加载和处理。 -
shiro.ini
配置文件 -
[users] zhangsan=123,admin [roles] admin=product:view,product:create,product:update,product:delete
-
@Slf4j public class ShiroTest01 { @Test public void run() { // 1. 创建shiro的 SecurityManager 核心安全管理器 DefaultSecurityManager securityManager = new DefaultSecurityManager(); // 2. 设置用户的权限信息,从shiro.ini配置文件中读取 IniRealm realm = new IniRealm("classpath:shiro.ini"); securityManager.setRealm(realm); // 3. 赋值安全管理器给shiro工具类,设置shiro的运行环境中 SecurityUtils.setSecurityManager(securityManager); // 4.创建一个subject主体实例 Subject subject = SecurityUtils.getSubject(); // 5. 创建登录校验的用户名和密码token UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123"); // 6. 进行用户登录 , 通过主体进行 Authenticator认证 和 Authorize授权 try { subject.login(token); } catch (IncorrectCredentialsException e) { log.error("用户密码错误===>" , e); } catch (AuthenticationException e) { log.error("系统错误===>" , e); } // 7. 打印登录情况 log.info("是否登录===>{}" , subject.isAuthenticated()); log.info("登录信息===>{}" , subject.getPrincipal()); // 8. 权限校验 log.info("是否有权限===>{}" , subject.isPermitted("product:view","product:list")); log.info("是否拥有某角色信息===>{}" , subject.hasRole("admin")); // 9. 登出系统 subject.logout(); // 10. 查看是否登出 log.info("是否登录===>{}" , subject.isAuthenticated()); } }
基础总结
- Shiro是以前在学校当做课外学习的内容,但工作之后已经有两三年没有再碰过了,而2021今年开始要好好充电学习,先拿Shiro开刀。
- 在工作中项目也是用到shiro来权限管理,功能还是十分的实用。一般工作中都是把用户权限等信息储存在数据库中,在shiro初始化配置时读取数据库,来配置角色和权限。
- 本节第一节只是日常学习笔记记录,示例代码也是最简单最基础的单元测试类。
Shiro基础扩展功能
自定义Realm
-
Realm
用于存储 用户的认证和权限 等相关信息,在正常业务中一般这些信息都存储在数据库中,所以我们需要自定义实现Realm
从数据库中获取用户的认证授权信息,进行自定义处理。 -
Realm
主要继承AuthorizingRealm
来实现自定义操作。 -
AuthorizingRealm(权限)
继承于AuthenticatingRealm(认证)
继承于CachingRealm
实现缓存管理,三者父子继承关系,一般继承AuthorizingRealm
来进行自定义处理。 -
代码示例:
-
@Slf4j public class ShiroRealmConfiguration extends AuthorizingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 自定义登录验证方法 log.info("自定义Realm验证用户信息=========>"); // 1. token转为 usernameAndPasswordToken 获取用户信息 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; // 2. 获取用户信息,进而去数据库中校验用户 String username = token.getUsername(); char[] password = token.getPassword(); if (!"zhangsan".equals(username)) { throw new UnknownAccountException(); } System.out.println(token.getPrincipal()); System.out.println(token.getCredentials()); // 3. 进行密码校验,加密的密码直接通过 SimpleAuthenticationInfo 可以内部进行校验密码 return new SimpleAuthenticationInfo(token.getPrincipal(),token.getCredentials(),username); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // 自定义权限授权方法 log.info("自定义Realm验证权限=========>"); // 1. 获取主体信息,此信息由认证SimpleAuthenticationInfo 第一个参数配置的对象 String userName = (String) principalCollection.getPrimaryPrincipal(); if ("zhangsan".equals(userName)) { // 2. 配置各类信息 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setRoles(Collections.singleton("admin")); // 赋值多角色 // 多权限字符串 Set<String> permissionSet = new HashSet<String>(); permissionSet.add("product:view"); permissionSet.add("product:create"); info.setStringPermissions(permissionSet); return info; } return null; } }
Shiro异常类
- Shiro提供了一系列自定义的异常类信息,用于在业务中判断用户密码错误,用户信息不存在等异常情况。
密码加密
散列算法
-
散列算法一般用于生成文本的散列摘要信息,而无法再逆转为原始明文内容。
- 散列算法不可逆
- 散列算法常用于对密码的加密
- 常用散列算法主要用 MD5 和 SHA
-
一般散列算法需要提供一个
salt(盐)
与 原始明文生成摘要信息,为了更加安全。 -
// 列如 密码 123456 通过 MD5 散列加密成 abc 通过MD5解密网站就可以解密成明文原始 加上 salt(盐,一个随机数) 则可以生成不同的摘要信息,更加安全 SimpleHash hash01 = new SimpleHash("MD5", "123456", "AK"); log.info("Shiro进行MD5散列加密===>{}" , hash01); // hashIterations参数表示 散列迭代次数,类似递归方法重复散列计算 Md5Hash hash02 = new Md5Hash("123456", "AK" , 2); log.info("Shiro进行MD5散列加密,并且散列两次===>{}" , hash02); // 盐值用原文密码,通过Shiro转换盐值的方法 ByteSource salt = ByteSource.Util.bytes("123456"); log.info("通过Shiro的方法获取盐值===>{}",salt.toString());
密码比较
-
一般存储在数据库中用户密码都是 MD5加密后,不可逆的摘要信息存储。
-
将数据库输入的密码进行同样的加密处理后,与数据库密文密码进行比较来判断。
-
// 在自定义继承 AuthorizingRealm 的 realm 中进行密码的判断 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 自定义登录验证方法 // ****************************** // 3. 进行密码校验,加密的密码直接通过 SimpleAuthenticationInfo 可以内部进行校验密码 return new SimpleAuthenticationInfo(token.getPrincipal(),"ea48576f30be1669971699c09ad05c94", ByteSource.Util.bytes(token.getPassword()),"user"); } // 将MD5加密后的密文 和 之前所用的salt ,Shiro自动进行密码校验
缓存管理
-
没有缓存,shiro每次调用判断是否权限,都会走
Realm
中doGetAuthorizationInfo
权限校验的方法。 -
Shiro默认是关闭缓存管理器的,需要配置赋值给
SecurityManager
核心安全管理器 -
缓存之前:
-
-
缓存之后
-
内置缓存管理器
-
Shiro有内置自己的缓存管理器
CacheManager
-
创建
MemoryConstrainedCacheManager()
实现类对象即可 -
// 设置缓存管理器, 赋值给 SecurityManager 核心管理器 CacheManager cacheManager = new MemoryConstrainedCacheManager(); securityManager.setCacheManager(cacheManager);
第三方缓存支持
-
Shiro的内置缓存管理器,性能低扩展性差,有时我们需要第三方的Cache缓存管理器。
-
比如常用的Redis缓存
-
<!-- shiro-redis 单机方法 --> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>3.3.1</version> </dependency> <!-- springboot中使用 --> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis-spring-boot-starter</artifactId> <version>3.3.1</version> </dependency>
-
代码示例
-
RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(new RedisManager()); securityManager.setCacheManager(redisCacheManager);
权限数据模型
RABC
权限模型设计。数据库表相关的模型设计。- 用户 —> 多个角色 —> 多个权限。 最少需要五张表来构建权限模型结构。
资源表设计
-
模型中最复杂的是资源表,企业中的一套资源表设计。
- url: 请求的后端路径,后端通过访问路径来判断需要的不同角色进行校验。
- route:前端路由,前端通过路由来判断是否隐藏。
- level:表示级别
- 1: 表示一级菜单
- 2: 表示二级菜单
- 3:依次类推(类似新增,修改等按钮视为最后一级菜单)
- type:表示是菜单页面还是按钮
-
CREATE TABLE `sys_menu_info` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `parent_id` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '上级资源ID', `url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'url', `router` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '前端路径', `title` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '资源名称', `level` int DEFAULT NULL COMMENT '资源级别1-一级菜单,2-二级菜单,3-三级菜单', `sort_no` int DEFAULT NULL COMMENT '排序', `type` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型 0-菜单,1-按钮(table,button)', `status` int NOT NULL DEFAULT '1' COMMENT '状态0-无效,1-有效', `remark` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', `create_time` datetime NOT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=277 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统管理-权限资源表 ';
-
数据结构:
-
Realm接口架构图
Shiro整体结构图
-
Shiro整体结构图
-
-
Subject: 主体相当于与客户端交互的用户信息 SecurityManager: Shiro的核心管理器,管理者Shiro的一切,认证授权会话缓存.等 Authenticator: 认证器,负责主体Subject的认证登录操作 Authorizer: 授权器,负责主体Subject的权限相关验证 Realm: 可以有多个Realm,是Shiro的安全认证数据源,可以自定义从数据库获取 SessionManager: 会话管理器,管理者Session的生命周期,Session是客户端与服务端交互的关联信息,集群模式下可以使用Redis作为分布式会话管理 SessionDao: 用来操作Session会话的CRUD,向SessionManager负责,可以自定义实现存储到数据库或者redis中。 CacheManager: 缓存管理器,管理比如用户,角色,权限等缓存。可以提高系统的性能。 Cryptography: 密码模块,提供的一些常见的加密组件
-
ShiroSecurityManager管理者所有的Subject(登录信息) , Realm是认证安全数据源
-
项目文件 shiro-test01
- 链接:https://pan.baidu.com/s/1OotLAOubzxpZDpPLpjdFuw
提取码:bk3p