shiro的理解

Shiro

权限管理中,使用的最多的,还是 基于角色的控制访问 RBAC

而实现权限管理,我们可以选择自己实现,也可以选择使用一些已经封装好的框架

两种常用的 权限管理框架 :

Apache Shiro

Spring Security

Shiro 概述

Shiro的作用

shiro 可以帮我们做什么 :

  • 过滤器拦截
  • 登录认证
  • 系统注销
  • 权限校验(代码,注解,标签)
  • 密码加密
  • 数据缓存

Shiro的架构

在这里插入图片描述

Shiro的三个核心组件 :

Subject ; SecurityManager ; Realms

Subject : 即 当前操作用户 , 但是,在 shiro 中, Subject 这一概念不仅仅指人,也可以是第三方进程,后台账户,或其他类似事物,他仅仅意味着 “当前跟软件交互的东西” ; 在登录过后,在程序的任意位置,可以使用 Subject currentUser = SecurityUtils.getSubject() 获取到 subject 主体对象,类似于 Employee emp = UserContext.getUser()

Subject 代表了当前用户的安全操作, SecurityManager 则管理所有用户的安全操作

SecurityManager : 是 Shiro 框架的核心,典型的Facade模式, shiro 通过SecurityManager来管理内部组件实例,并通过他来提供安全管理的各种服务,真正的业务逻辑之间的分发调度,就是交给安全管理器来进行

**Realm : ** Realm 充当了 shiro 与应用安全数据间的桥梁,或者说连接器,也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,shiro会从应用配置的Realm 中查找用户及其权限信息 , 用来提供数据的,不做密码判断,也不做权限或者角色的判断

从这个意义上说,Realm 实质上是一个安全的DAO,他封装了数据源的连接细节,并在需要时将相关数据提供给shiro,当配置 shiro 时,你必须至少指定一个 Realm ,用于认证/授权 ; 也可以配置多个Realm ,但是至少要配置一个 Realm

shiro 内置了可以连接大量安全数据源(也叫作目录)的 Realm , 如 LDAP ,关系数据库(JDBC) , 类似 INI 的文本配置资源以及属性文件等,如果缺省的 Realm 不能满足需求,还可以插入代表自定义数据源的自己的 Realm 实现

除了这三个核心组件,还有以下几个组件:

Authenticator : 认证器 , 用于认证,从 Realm 数据源取得数据后,进行执行认证流程处理

Authorizer : 授权器 : 用户访问控制授权,决定用户是否拥有执行指定操作的权限

SessionManager (会话管理器):Shiro 与生俱来就支持会话管理,这在安全类框架中都是独一无二的功能。即便不存在 web 容器环境,shiro 都可以使用自己的会话管理机制,提供相同的会话 API。

CacheManager (缓存管理器):用于缓存认证授权信息等。

Cryptography(加密组件): Shiro 提供了一个用于加密解密的工具包。

Shiro的身份认证流程

  1. Subject进行 login() 登录操作 , 参数是封装了用户信息的 token
  2. Security Manager 进行登录操作
  3. Security Manager 委托给 Authenticator 进行认证逻辑处理
  4. 调用 Authentication Strategy 进行多 Realm 身份验证
  5. 调用对应 Realm 进行登录校验 , 认证成功则返回用户属性,认证失败则抛出异常
    在这里插入图片描述

在这个过程中,常见的异常可能会有 :

DisabledAccountException(禁用的帐号)

LockedAccountException(锁定的帐号)

UnknownAccountException(错误的帐号)

ExcessiveAttemptsException(登录失败次数过多)

IncorrectCredentialsException (错误的凭证)

ExpiredCredentialsException(过期的凭证)

UnauthorizedException (不具备该权限)

在整个过程中,Shiro帮我们做了很多事,我们只需要负责提供 realm 数据源即可,其他的不需要我们考虑,账号和密码都是 shiro 负责进行判断的

自定义数据源 Shiro

在开发中,自定义数据源很多

自定义的数据源必须要继承 AuthorizingRealm 类,并且复写其中的两个方法 :

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){}
/*
负责提供授权信息
在判断有没有某个权限/某个角色时,会调用这个方法:
1 > subject.hasRole 或者 subject.isPermitted
2 > @RequiredRoles 在方法上加注解的时候
3 > [@shiro.hasPermission name = "admin"][/@shiro.hasPermission] 在页面上加 shiro 标签的时候
这个方法返回的参数,封装的是当前登录的用户所拥有的角色和权限数据
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){}
/*
负责提供认证信息
返回值 SimpleAuthenticationInfo 是 AuthenticationInfo 接口的实现类
返回时可以封装3个或者4个参数
第一个参数:传入user对象,也是用户的身份信息,用来跟subject绑定在一起,方便在登录后随时获取登录对象
第二个参数:从数据库中获取的password,再用来和token中的password进行比对,匹配上了就通过,否则报异常
第三个参数:盐,用于加密密码对比
第四个参数:当前数据源的名字,用来做一个标识:该数据是从哪个数据源来的
*/

在这里插入图片描述

shiro的注解式权限验证

使用shiro框架,我们在权限方法上贴上 @RequiresPermission 注解

注解中写上权限表达式,代表某个登录用户需要有这个权限,才能访问这个方法对应的映射界面

在 @RequiresPermission 注解中,可以写多个权限表达式,他们之间默认的逻辑关系是 与

但是我们需要能够获取到权限的名称,这样才能把响应权限展示给用户看,这应该怎么获取?

利用上面那个特性,我们在注解中增加一个 权限名称name , 然后将两个权限表达式之间的逻辑关系设置为 OR : 或 , 代表只要有这两个权限之间的一个就可以访问对应映射:

	//处理查询所有部门请求
    @RequestMapping("/list")
    // 代表需要有这个权限,才能访问到这个界面
    // 为了方便获取权限名称,将注解中增加一个name,用来获取,但是为了解决逻辑问题,将两个权限之间的关系修改为 或 OR
    @RequiresPermissions(value = {"department:list","部门页面"},logical = Logical.OR)
    public String list(Model model, QueryObject qo){
        model.addAttribute("pageInfo", departmentService.query(qo));
        return "department/list";
    }

注意 :

我们从Spring容器中获取到的控制器 Controller 对象是代理对象 , 这个代理对象类使用的代理方式是 CGLIB 动态代理,于是他继承了 原Controller 类,这样会导致我们 遍历获取到的方法,然后判断这些方法有没有贴权限注解 这个操作失败,因为继承是无法继承到方法上面的注解的 , 所以我们在遍历每个控制器获取他们的字节码对象时,应该获取他的父类,也就是原本的 Controller 类 :

在这里插入图片描述

在 freemarker 界面使用 shiro 标签进行权限相关控制

常用标签:

authenticated 标签:已认证通过的用户。

<@shiro.authenticated> </@shiro.authenticated>

notAuthenticated 标签:未认证通过的用户。与 authenticated 标签相对。

<@shiro.notAuthenticated></@shiro.notAuthenticated>

principal 标签:输出当前用户信息,通常为登录帐号信息

后台是直接将整个员工对象作为身份信息的,所以这里可以直接访问他的 name 属性得到员工的姓名

<@shiro.principal property="name" />

对应realm中返回的SimpleAuthenticationInfo对象的第一个参数

new SimpleAuthenticationInfo(employee,employee.getPassword(),this.getName());

hasRole 标签:验证当前用户是否拥有该角色

<@shiro.hasRole name="admin">Hello admin!</@shiro.hasRole>

hasAnyRoles 标签:验证当前用户是否拥有这些角色中的任何一个,角色之间逗号分隔

<@shiro.hasAnyRoles name="admin,user,operator">Hello admin</@shiro.hasAnyRoles>

hasPermission 标签:验证当前用户是否拥有该权限

<@shiro.hasPermission name="department:delete">删除</@shiro.hasPermission>

Shiro 的加密功能

用户的数据以明文的方式存储在数据库中,这样开发人员或者一些技术人员利用一些手段可以获取到他们的一些私密信息比如密码等,这样是很不安全的

Shiro提供了对信息进行加密的操作 , 加密过后的信息,以密文的方式存储在数据库中

使用 MD5 算法,对密码进行加密 :

@Test
public void testMD5() throws Exception{ 
    Md5Hash hash = new Md5Hash("1","admin",3);
	System.out.println(hash);//f3559efea469bd6de83d27d4284b4a7a
}

上面的方法 Md5Hash 中 :

  • 第一个参数表示要加密的东西,密码等
  • 第二个参数表示 , 根据盐和加密对象,一起对对象进行加密,一般选择用户唯一的数据当做盐,比如手机号码,身份证号码等等
  • 第三个参数表示 要进行加密的次数

有了加密算法,我们就需要对要存储的用户数据进行加密,比如在保存新用户时,将他的密码进行加密保存 :
在这里插入图片描述

但是这样会导致登录时,也需要对用户输入的密码进行加密操作,然后和数据库中的密码相匹配 :
在这里插入图片描述

Shiro 的缓存功能

因为在 Shiro 中,我们一旦进行 权限的控制 / 判断 , 都会去调用 Realm 中的

doGetAuthorizationInfo 方法,获取用户的权限信息,然后就需要跑到数据库去进行查询,这样子很影响性能

所以我们在第一次授权之后 , 将授权信息存储到缓存中,下一次判断权限时,直接在缓存中读取,避免频繁访问数据库

问题

为什么要自定义数据源 ?

  • 因为默认的数据源无法满足我们的需求,所以需要自定义数据源;

为什么要做密码加密?

  • 主要是为了保证系统中数据的安全性,加密过后,数据库中的密码变成了密文形式,不会轻易被他人破解

为什么要加缓存功能?

  • 为了避免在进行权限验证/判断时频繁访问数据库,影响程序性能

为什么登陆要用 subject.login() 方法

  • 使用该方法会间接调用数据源 Realm 中的

    doGetAuthenticationInfo(AuthenticationToken token) 方法 , 根据我们在数据库中获取的信息(存放在 info 中) 和 调用login方法时传递的 token 中的信息进行比对

Controller上贴了shiro的注解为什么会影响重新加载权限功能?

么要加缓存功能?**

  • 为了避免在进行权限验证/判断时频繁访问数据库,影响程序性能

为什么登陆要用 subject.login() 方法

  • 使用该方法会间接调用数据源 Realm 中的

    doGetAuthenticationInfo(AuthenticationToken token) 方法 , 根据我们在数据库中获取的信息(存放在 info 中) 和 调用login方法时传递的 token 中的信息进行比对

Controller上贴了shiro的注解为什么会影响重新加载权限功能?

  • 贴了 shiro 的 @RequiresPermission 注解,会导致从Spring容器中获取到的是这个 Controller 的代理对象 , 采用 CGLIB 动态代理方式,而代理对象的方法是无法继承到父类方法上的注解的,这样重新加载权限功能就失效了.
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值