Shiro权限框架
认证:判断用户是否存在 判断账号和密码是否正确
授权:就是给你一些权限,能够访问哪些资源,授权之前先进行认证。
什么是权限管理
权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源,不多不少。权限管理几乎出现在任何系统里面,只要有用户和密码的系统。常用于后台管理系统。
通用权限管理,一般指的是五张表:权限表、角色表、用户表、权限角色表、用户角色表
—>用户登录时,通过用户名---->含有的角色----->查询出含有的权限---->可以操做的资源
之前的项目:智游公寓、智游客户关系管理系统、智游在线视频、智游医院、智游众筹
权限管理一般包括用户身份认证和授权两个部分,简称认证授权。
对于需要访问控制的资源用户需要首先经过身份认证,
认证通过后,用户具有该资源的访问权限
用户身份认证
认证
概念
身份认证就是判断一个用户是否为合法用户的处理过程。
最常用的简单身份认证方式就是通过对用户输入的名户名和口令,
查看是否与系统中的储存的用户名和口令一致,来判断用户身份是否正确。
现实中,指纹系统、刷卡系统,本质都是判断用户名和密码是否正确。
身份认证流程
关键对象
- Subject:主体,访问系统的用户,程序等,一般情况下需要进行认证的都称之为“主体”。
- Principal:身份信息,是主体进行身份认证的一种表示,这个 身份一般具有唯一性。
- credential:凭证信息,是只有主体才知道的安全信息,例如:密码、密钥、口令等
授权
概念
收授权即访问控制,就是控制谁能够访问哪些资源。
主体进行身份认证后需要分配权限方可访问系统资源。
对于某些资源没有权限是无法访问的。
授权流程
关键对象
授权可以理解为:谁对什么进行了怎样操作
谁: subject: 主体,需要访问系统中的资源
什么:Resource 比如 :包括资源的类型和资源的实例(资源类型: 删除用户 资源实例: 删除某个用户或者某些具体的用户 )
怎样:Permission:权限或者许可,规定了主体对资源的操作许可。
注意:权限离开资源没有意义的,通过权限可知主体对系统中的哪些资源都有哪些操作许可。
权限分为粗颗粒和细颗粒
粗颗粒权限是指资源类型的权限,细颗粒权限是指资源实例的权限。
比如说添加用户属于资源类型(粗颗粒),具体的某个操作属于资源实例(细颗粒)
比如说:班长可以管理整个班同学,而组长只能管理分配的组内的某几个人。
主体、权限、资源关系图:
权限模型
主体、权限、资源都是通过数据模型来呈现。
主体(账号和密码)
资源 (资源名称、访问地址)
权限 (权限名称,资源ID)
角色 (角色名称)
角色权限关系 (角色 id和权限ID)
主体和角色关系(主体ID和角色ID)
在企业开发中,一般情况下会把权限和资源合并成一张表
资源(资源名称,资源访问url地址)
权限 (权限名称,资源ID)
合并:
权限(权限名称,资源名称,资源访问url地址)
权限分配
对主体分配权限,主体只允许在自己权限范围内对资源进行操作
权限分配的数据需要进行持久化,根据模型图来搭建,并将用户的权限信息储存在数据库中。
权限控制
用户拥有权限就可以操作权限范围内的资源,对于系统来说不知道主体是否拥有访问权限需要对用户的访问进行控制。
第一种:基于角色的权限控制
RBAC基于角色的访问控制,是以角色为中心进行访问控制。
缺点:角色进行访问控制颗粒度较粗,如果主体角色发生变更,那么需要修改原有代码,可扩展性较差。
比如说:你是一个群主
第二种:基于资源的权限控制
RBAC基于资源的访问控制,是以资源为中心进行访问控制。
比如说:主体必须拥有查询工资的权限方可查询员工信息
优点:
权限标识是提前设定好的
查询工资的权限对应的角色---->总经理
查询工资的权限对应的角色---->总经理和部门经理
将查询工资的权限添加到部门经理角色的权限列表中,原有代码不需要发生改变,系统可扩展性较好。
权限管理解决方案
第一种:使用粗颗粒和细颗粒进行控制
第二种:基于url拦截
基于url拦截的是企业中比较常用的一种解决方案,
实现思路:
将系统操作的每个url配置到权限表中
将权限对应到角色,将角色分配给用户,用户访问系统资源通过Filter进行拦截
过滤器获取到用户访问的url,只要访问的url是用户分配角色中的url则放行让用户继续访问
第三种:使用权限管理框架 Spring Security 、Shiro
使用 权限管理框架进行系统开发
节省系统开发时间
可以节省人力成本
提供了完善的认证和授权功能
有利于系统的维护,方便的系统的扩展
Shiro
概述
Apache旗下的一款开源免费的安全框架
它将软件系统的安全认证相关的功能抽离出来,实现用户身份认证,权限授权,加密
会话管理等功能,组成了一个通用的安全认证框架。
为什么要学Shiro?
使用shiro可以快速的完成认证、授权等功能的开发,降低系统的成本。
shiro使用非常广泛,既可以在web应用中使用,也可以在非web环境中使用,集群分布式中使用也较多
Java领域中的Security使用稍微复杂一点,依赖于Spring环境,而shiro相对较独立一些。
shiro权限框架
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G31tl2zn-1617202304891)(C:\Users\admin\Desktop\shiro权限框架.jpg)]
-
Subject
主体,外部应用和subject进行交互,subject记录了当前操作用户
用户可以理解为一个浏览器请求的用户,也可以理解为一个正在运行的程序
Subject是shiro框架中的一个接口,该接口中定义了很多认证和授权的相关方法,外部程序可以通过subject
进行认证和授权,而subject是通过SecurityManager 安全管理器进行认证授权。
-
SecurityManager
安全管理器,对全部的 subject进行安全管理,它是整个shiro的核心,负责对所有的subject进行安全管理
通过SecurityManager 可以完成对subject的认证和授权
实质上认证需要通过Authenicator组件来实现的,授权需要通过Authorizer组件来实现的,会话管理通过SessionManager组件来实现等。。
SecurityManager也是一个接口,继承了Authenicator、AuthorizerSessionManager这三个接口。
-
Authenicator
认证器,主要是对用户身份进行认证,Authenicator是一个接口,shiro提供了Authenicator的实现类
通过它的实现类基本上就可以解决大部分认证的问题。
也可以自定义认证器。
-
Authorizer
授权器,用户通过认证器认证通过后,在访问系统资源时需要通过授权器判断用户是否有此资源的操作权限。
-
realm
领域,相当于数据库,SecurityManager进行安全认证需要通过读取realm中的信息判断用户是否有此权限资源,
用户身份数据要么在数据库中存储,要么就在realm中存储
如果是在数据库中,realm需要从数据库中获取用户的身份信息
-
SessionManager
会话管理器,shiro框架它不依赖与web容器中session,shiro它可以在非web应用中使用
也可以在分布式应用中使用,如果是在分布式应用中,可以帮助实现【单点登录】。
-
SessionDAO
会话DAO,它可以将session存储到数据库中,也可以通过jdbc把session存储到数据库
-
CacheManager
缓存管理器,将用户权限数据存储到缓存中,可以提高的访问性能。
-
Cryptography
密码加密器。
快速入门:
认证:
realm 领域 存储权限资源
securityManager 安全管理器
subject 主体身份信息 账号、密码
开发步骤:
-
导包:
<!--导入shiro相关坐标依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.0</version> </dependency>
-
新建shiro.ini文件
[users] #定义用户名和密码 第一个root是账户 第二个root是密码 #账户名=密码值 root=root admin=admin
-
新建测试类 TestShiro
@Test public void test01() { // realm // Subject // SecurityManager // 1.构建SecurityManager实例对象 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); // 2. 将SecurityManager添加到运行环境中 SecurityUtils.setSecurityManager(securityManager); // 3. 获取subject主体信息 从realm中 shiro.ini文件 Subject subject = SecurityUtils.getSubject(); // 4. 创建token令牌 令牌中包含了用户输入的账户和密码 UsernamePasswordToken token = new UsernamePasswordToken("root", "root"); /** * 如果账户不存在,程序会抛出org.apache.shiro.authc.UnknownAccountException 未知账户异常 * 如果账户存在,密码错误,程序会抛出org.apache.shiro.authc.IncorrectCredentialsException 凭证不正确异常 */ // 5. 进行认证 try{ subject.login(token);// login 登录 System.out.println("认证通过。。。。"); }catch (UnknownAccountException e) { System.out.println("账户不存在!"); }catch (IncorrectCredentialsException e) { System.out.println("密码拼写错误!"); } // 退出 会把认证的信息清除掉 subject.logout(); // 判断是否认证通过 boolean authenticated = subject.isAuthenticated(); System.out.println("认证结果为:" +authenticated);// true成功 false失败 }
-
自定义Realm
开发步骤:
-
第一步:新建自定义类MyRealm
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 1. 先获取token的身份信息 用户输入的令牌 账号 String username = (String) token.getPrincipal(); // 2. 认证 // 连接数据库比对账号是否正确 boolean select u_pwd from user where u_name = 'username'; /* boolean authenticated = true/false; if (authenticated == false) { return null; 没有查询到对应的账户信息 返回一个空 } */ // 如果查到了 就获取到了密码值 String pwd = "root";// root就是查询查询出来的密码值 SimpleAuthenticationInfo myRealm = new SimpleAuthenticationInfo(username, pwd, "myRealm"); return myRealm; }
-
第二步:新建shiro_myRealm.ini文件
[main] myRealm=com.zhiyou.video.utils.MyRealm securityManager.realms=$myRealm
-
第三步:测试自定义的reaml
-
授权
快速入门:
开发步骤:
-
第一步,新建权限资源文件 shiro_privilege.ini文件
[users] #账户=密码值,角色名称,角色名称 root=root,role1,role2 admin=admin,role2 [roles] #角色名称=权限资源,...,...,.. #权限资源有两种方式:第一种方式:/user/addUser.action # 第二种方式:user:addUser.action user:updateUser.action role1=/user/addUser.action,/user/deleteUser.action,/user/updateUser.action role2=/user/selectAllUser.action
-
第二步:测试
// 测试 授权 @Test public void test03() { // 1. 构建SecurityManager实例对象 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_privilege.ini"); SecurityManager securityManager = factory.getInstance(); // 2. 将SecurityManager添加到运行环境中 SecurityUtils.setSecurityManager(securityManager); // 3. 获取subject主体信息 从realm中 shiro.ini文件 Subject subject = SecurityUtils.getSubject(); // 4. 创建token令牌 令牌中包含了用户输入的账户和密码 UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin"); // 授权的前提是先进行认证 // 5. 进行认证 try{ subject.login(token);// login 登录 System.out.println("认证通过。。。。"); }catch (UnknownAccountException e) { System.out.println("账户不存在!"); }catch (IncorrectCredentialsException e) { System.out.println("密码拼写错误!"); } // 判断是否认证通过 boolean authenticated = subject.isAuthenticated(); System.out.println("认证结果为:" +authenticated);// true成功 false失败 // 授权 查看该用户是否拥有操作此资源的访问权限 String[] strs = {"/user/selectAllUser.action","/user/updateUser.action"}; boolean[] permitted = subject.isPermitted(strs); for (int i = 0; i < permitted.length; i++) { System.out.println(strs[i] + "权限为: "+ permitted[i]); } }
-
自定义MyRealm 进行授权
-
第一步:修改doGetAuthorizationInfo(PrincipalCollection principalCollection)该方法
//授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // 1. 获取身份信息 String username = (String) principalCollection.getPrimaryPrincipal(); // 2. 连接数据库查询该账号是否存在 /* 2.1 先根据用户名查询 user、role、user_role表 可以获取该用户对应得角色信息 return null */ List<String> roles = new ArrayList<>(); roles.add("CTO");// 首席技术官 技术总裁 roles.add("CEO");// 首席执行官 总经理 张勇 马云 /* 2.2 再根据角色查询对应的权限资源 role、privilege、role_privilege */ // 假如你获取到了数据库对应的权限资源 List<String> privileges = new ArrayList<>(); privileges.add("/user/addUser.action"); privileges.add("/user/selectAllUser.action"); // 3. 把角色和权限进行绑定 SimpleAuthorizationInfo SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 添加角色信息 info.addRoles(roles); info.addStringPermissions(privileges); return info; }
-
第二步:使用创建的shiro_myRealm.ini文件
-
第三部:测试
// 测试 自定义MyRealm 授权 @Test public void test04() { // 1. 构建SecurityManager实例对象 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_myRealm.ini"); SecurityManager securityManager = factory.getInstance(); // 2. 将SecurityManager添加到运行环境中 SecurityUtils.setSecurityManager(securityManager); // 3. 获取subject主体信息 从realm中 shiro.ini文件 Subject subject = SecurityUtils.getSubject(); // 4. 创建token令牌 令牌中包含了用户输入的账户和密码 UsernamePasswordToken token = new UsernamePasswordToken("admin", "root"); // 授权的前提是先进行认证 // 5. 进行认证 try{ subject.login(token);// login 登录 System.out.println("认证通过。。。。"); }catch (UnknownAccountException e) { System.out.println("账户不存在!"); }catch (IncorrectCredentialsException e) { System.out.println("密码拼写错误!"); } // 判断是否认证通过 boolean authenticated = subject.isAuthenticated(); System.out.println("认证结果为:" +authenticated);// true成功 false失败 // 查看该用户拥有哪些角色信息 boolean ceo = subject.hasRole("CEO"); System.out.println("CEO:" + ceo); // 授权 查看该用户是否拥有操作此资源的访问权限 String[] strs = {"/user/selectAllUser.action","/user/updateUser.action"}; boolean[] permitted = subject.isPermitted(strs); for (int i = 0; i < permitted.length; i++) { System.out.println(strs[i] + "权限为: "+ permitted[i]); } }
-
-
**如果是自定义的MyRealm,那么认证和授权走自定义的MyRealm类的认证和授权方法。