一、前言

  要开始接触公司另外一个项目了,RX和我说了下整个项目框架的结构,其中提到权限的控制是通过shiro来处理的,对我而言又是一个全新的知识点,于是今天花了一点时间去学习shiro的使用,看了好几篇博客,觉得有篇CSDN博文写的不错,于是在这边转载一下~

二、正文

1.Shiro基本架构

1.1 什么是Shiro 
Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能:

  • 认证 - 用户身份识别,常被称为用户“登录”;
  • 授权 - 访问控制;
  • 密码加密 - 保护或隐藏数据防止被偷窥;
  • 会话管理 - 每用户相关的时间敏感的状态。

对于任何一个应用程序,Shiro都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro要简单的多。

1.2 Shiro的架构介绍 
首先,来了解一下Shiro的三个核心组件:Subject, SecurityManager 和 Realms. 如下图:

Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。 
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。

SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。 Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

Shiro完整架构图

除前文所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括: 
Authenticator :认证就是核实用户身份的过程。这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。 
Authorizer :授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。 
SessionManager :在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。 
CacheManager :对Shiro的其他组件提供缓存支持

2.Shiro的认证机制

认证就是验证用户身份的过程。在认证过程中,用户需要提交实体信息(Principals)和凭据信息(Credentials)以检验用户是否合法。最常见的“实体/凭证”组合便是“用户名/密码”组合。

2.1 Shiro认证过程

1)收集实体/凭据信息

//Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//”Remember Me” built-in:
token.setRememberMe(true);

UsernamePasswordToken支持最常见的用户名/密码的认证机制。同时,由于它实现了RememberMeAuthenticationToken接口,我们可以通过令牌设置“记住我”的功能。 
但是,“已记住”和“已认证”是有区别的: 
已记住的用户仅仅是非匿名用户,你可以通过subject.getPrincipals()获取用户信息。但是它并非是完全认证通过的用户,当你访问需要认证用户的功能时,你仍然需要重新提交认证信息。 
这一区别可以参考亚马逊网站,网站会默认记住登录的用户,再次访问网站时,对于非敏感的页面功能,页面上会显示记住的用户信息,但是当你访问网站账户信息时仍然需要再次进行登录认证。

2)提交实体/凭据信息

Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);

收集了实体/凭据信息之后,我们可以通过SecurityUtils工具类,获取当前的用户,然后通过调用login方法提交认证。

3)认证处理

try {
currentUser.login(token);
} catch ( UnknownAccountException uae ) { ...
} catch ( IncorrectCredentialsException ice ) { ...
} catch ( LockedAccountException lae ) { ...
} catch ( ExcessiveAttemptsException eae ) { ...
} ... catch your own ...
} catch ( AuthenticationException ae ) {
//unexpected error?
}

如果login方法执行完毕且没有抛出任何异常信息,那么便认为用户认证通过。之后在应用程序任意地方调用SecurityUtils.getSubject() 都可以获取到当前认证通过的用户实例,使用subject.isAuthenticated()判断用户是否已验证都将返回true. 
相反,如果login方法执行过程中抛出异常,那么将认为认证失败。Shiro有着丰富的层次鲜明的异常类来描述认证失败的原因,如代码示例。

2.2 登出操作 
登出操作可以通过调用subject.logout()来删除你的登录信息,如:

currentUser.logout(); //removes all identifying information and invalidates their session too. 

当执行完登出操作后,Session信息将被清空,subject将被视作为匿名用户。

2.3 认证内部处理机制 
以上,是Shiro认证在应用程序中的处理过程,下面将详细解说Shiro认证的内部处理机制。

如上图,我们通过Shiro架构图的认证部分,来说明Shiro认证内部的处理顺序: 
1)应用程序构建了一个终端用户认证信息的AuthenticationToken 实例后,调用Subject.login方法。 
2)Subject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用securityManager.login(token)方法。 
3)SecurityManager接受到token(令牌)信息后会委托内置的Authenticator的实例(通常都是ModularRealmAuthenticator类的实例)调用authenticator.authenticate(token). ModularRealmAuthenticator在认证过程中会对设置的一个或多个Realm实例进行适配,它实际上为Shiro提供了一个可拔插的认证机制。 
4)如果在应用程序中配置了多个Realm,ModularRealmAuthenticator会根据配置的AuthenticationStrategy(认证策略)来进行多Realm的认证过程。在Realm被调用后,AuthenticationStrategy将对每一个Realm的结果作出响应。 
注:如果应用程序中仅配置了一个Realm,Realm将被直接调用而无需再配置认证策略。 
5)判断每一个Realm是否支持提交的token,如果支持,Realm将调用getAuthenticationInfo(token); getAuthenticationInfo 方法就是实际认证处理,我们通过覆盖Realm的doGetAuthenticationInfo方法来编写我们自定义的认证处理。

2.4 使用多个Realm的处理机制: 
1)Authenticator 
默认实现是ModularRealmAuthenticator,它既支持单一Realm也支持多个Realm。如果仅配置了一个Realm,ModularRealmAuthenticator 会直接调用该Realm处理认证信息,如果配置了多个Realm,它会根据认证策略来适配Realm,找到合适的Realm执行认证信息。 
自定义Authenticator的配置:

当应用程序配置了多个Realm时,ModularRealmAuthenticator将根据认证策略来判断认证成功或是失败。 
例如,如果只有一个Realm验证成功,而其他Realm验证失败,那么这次认证是否成功呢?如果大多数的Realm验证成功了,认证是否就认为成功呢?或者,一个Realm验证成功后,是否还需要判断其他Realm的结果?认证策略就是根据应用程序的需要对这些问题作出决断。 
认证策略是一个无状态的组件,在认证过程中会经过4次的调用:

  • 在所有Realm被调用之前
  • 在调用Realm的getAuthenticationInfo 方法之前
  • 在调用Realm的getAuthenticationInfo 方法之后
  • 在所有Realm被调用之后

认证策略的另外一项工作就是聚合所有Realm的结果信息封装至一个AuthenticationInfo实例中,并将此信息返回,以此作为Subject的身份信息。 
Shiro有3中认证策略的具体实现:

AtLeastOneSuccessfulStrategy 只要有一个(或更多)的Realm验证成功,那么认证将被视为成功
FirstSuccessfulStrategy 第一个Realm验证成功,整体认证将被视为成功,且后续Realm将被忽略
AllSuccessfulStrategy 所有Realm成功,认证才视为成功

ModularRealmAuthenticator 内置的认证策略默认实现是AtLeastOneSuccessfulStrategy 方式,因为这种方式也是被广泛使用的一种认证策略。当然,你也可以通过配置文件定义你需要的策略,如:

3)Realm的顺序 
由刚才提到的认证策略,可以看到Realm在ModularRealmAuthenticator 里面的顺序对认证是有影响的。 
ModularRealmAuthenticator 会读取配置在SecurityManager里的Realm。当执行认证是,它会遍历Realm集合,对所有支持提交的token的Realm调用getAuthenticationInfo 。 
因此,如果Realm的顺序对你使用的认证策略结果有影响,那么你应该在配置文件中明确定义Realm的顺序,如:

3.Shiro的授权机制

授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限。 
如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等。

3.1 授权的三要素

授权有着三个核心元素:权限、角色和用户。

权限 
权限是Apache Shiro安全机制最核心的元素。它在应用程序中明确声明了被允许的行为和表现。一个格式良好的权限声明可以清晰表达出用户对该资源拥有的权限。 
大多数的资源会支持典型的CRUD操作(create,read,update,delete),但是任何操作建立在特定的资源上才是有意义的。因此,权限声明的根本思想就是建立在资源以及操作上。 
而我们通过权限声明仅仅能了解这个权限可以在应用程序中做些什么,而不能确定谁拥有此权限。 
于是,我们就需要在应用程序中对用户和权限建立关联。 
通常的做法就是将权限分配给某个角色,然后将这个角色关联一个或多个用户。

权限声明及粒度 
Shiro权限声明通常是使用以冒号分隔的表达式。就像前文所讲,一个权限表达式可以清晰的指定资源类型,允许的操作,可访问的数据。同时,Shiro权限表达式支持简单的通配符,可以更加灵活的进行权限设置。 
下面以实例来说明权限表达式。 
可查询用户数据 
User:view 
可查询或编辑用户数据 
User:view,edit 
可对用户数据进行所有操作 
User:* 或 user 
可编辑id为123的用户数据 
User:edit:123

角色 
Shiro支持两种角色模式: 
1、传统角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。 
2、权限角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。 
下面将详细描述对两种角色模式的授权实现。

3.2 授权实现

Shiro支持三种方式实现授权过程:

  • 编码实现
  • 注解实现
  • JSP Taglib实现

》基于编码的授权实现

* 基于传统角色授权实现 
当需要验证用户是否拥有某个角色时,可以调用Subject 实例的hasRole*方法验证。

相关验证方法如下:

Subject方法 描述
hasRole(String roleName) 当用户拥有指定角色时,返回true
hasRoles(List<String> roleNames) 按照列表顺序返回相应的一个boolean值数组
hasAllRoles(Collection<String> roleNames) 如果用户拥有所有指定角色时,返回true

断言支持 
Shiro还支持以断言的方式进行授权验证。断言成功,不返回任何值,程序继续执行;断言失败时,将抛出异常信息。使用断言,可以使我们的代码更加简洁。

断言的相关方法:

Subject方法 描述
checkRole(String roleName) 断言用户是否拥有指定角色
checkRoles(Collection<String> roleNames) 断言用户是否拥有所有指定角色
checkRoles(String... roleNames) 对上一方法的方法重载

》基于权限角色授权实现 
相比传统角色模式,基于权限的角色模式耦合性要更低些,它不会因角色的改变而对源代码进行修改,因此,基于权限的角色模式是更好的访问控制方式。 
它的代码实现有以下几种实现方式: 

 

*基于权限对象的实现

创建org.apache.shiro.authz.Permission的实例,将该实例对象作为参数传递给Subject.isPermitted()进行验证。

相关方法如下:

Subject方法 描述
isPermitted(Permission p) Subject拥有制定权限时,返回treu
isPermitted(List<Permission> perms) 返回对应权限的boolean数组
isPermittedAll(Collection<Permission> perms) Subject拥有所有制定权限时,返回true

*基于字符串的实现

相比笨重的基于对象的实现方式,基于字符串的实现便显得更加简洁。

这里分别代表了 资源类型:操作:资源ID

类似基于对象的实现相关方法,基于字符串的实现相关方法: 
isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms)

基于权限对象的断言实现

基于字符串的断言实现

Subject方法 说明
checkPermission(Permission p) 断言用户是否拥有制定权限
checkPermission(String perm) 断言用户是否拥有制定权限
checkPermissions(Collection<Permission> perms) 断言用户是否拥有所有指定权限
checkPermissions(String... perms) 断言用户是否拥有所有指定权限

》基于注解的授权实现 
Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不同的配置。

相关的注解: 
@ RequiresAuthentication 
可以用户类/属性/方法,用于表明当前用户需是经过认证的用户。

表明该用户需为”guest”用户

@ RequiresPermissions 
当前用户需拥有制定权限

@ RequiresUser 
当前用户需为已认证用户或已记住用户

》基于JSP  TAG的授权实现 
Shiro提供了一套JSP标签库来实现页面级的授权控制。 
在使用Shiro标签库前,首先需要在JSP引入shiro标签:

下面一一介绍Shiro的标签: 
guest标签 
验证当前用户是否为“访客”,即未认证(包含未记住)的用户

认证通过或已记住的用户

authenticated标签 
已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。

notAuthenticated标签 
未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。

hasRole标签 
验证当前用户是否属于该角色

lacksRole标签 
与hasRole标签逻辑相反,当用户不属于该角色时验证通过

hasAnyRole标签 
验证当前用户是否属于以下任意一个角色。

hasPermission标签 
验证当前用户是否拥有制定权限

与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过

三、链接

1、http://blog.csdn.net/xiaoliuliu2050/article/details/54911480

四、联系本人

  为方便没有博客园账号的读者交流,特意建立一个企鹅群(纯公益,非利益相关),读者如果有对博文不明之处,欢迎加群交流:261746360,小杜比亚-博客园

shiro学习详解(开篇)的更多相关文章

  1. Shiro学习详解

    1.Shiro基本架构 一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户“登录”: 授权 ...

  2. 弹性布局学习-详解align-content(六)

    弹性布局学习-详解align-content(六)

  3. 弹性布局学习-详解 justify-content(三)

    弹性布局学习-详解 justify-content(三)

  4. 弹性布局学习-详解 flex-direction【决定主轴的方向】(二)

    弹性布局学习-详解 flex-direction[决定主轴的方向](二)

  5. 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权

    原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...

  6. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  7. 弹性布局学习-详解flex-wrap(五)

    目录 弹性布局学习-介绍(一)  弹性布局学习-详解 flex-direction[决定主轴的方向](二) 弹性布局学习-详解 justify-content(三) 弹性布局学习-详解 align-i ...

  8. 弹性布局学习-详解 align-items(四)

    目录 弹性布局学习-介绍(一)  弹性布局学习-详解 flex-direction[决定主轴的方向](二) 弹性布局学习-详解 justify-content(三) 弹性布局学习-详解 align-i ...

  9. Github新手使用学习详解

    Github新手使用学习详解 (一)git的使用学习 首先下载git bash,安装一路默认选择即可. 安装好后第一步,获取ssh key,即属于自己的密钥: 打开Git bash后输入以下代码: $ ...

随机推荐

  1. java四种访问权限

    java有四种访问权限,它们各自的范围如下图所示 当下列访问修饰符修饰字段和方法时: private 任意位置的子类不可以访问从父类继承的private字段和方法.这里所说的访问包括通过super关键 ...

  2. leetcode笔记11 First Unique Character in a String

    题目描述: Given a string, find the first non-repeating character in it and return it's index. If it does ...

  3. HIS系统两种收费模式比较:前计费和后计费

    一.药品 a.前计费:审核(临时医嘱)或者分解(长期医嘱)计费 退费处理方式,1)如果是还未发药,则护士站直接退费;2)如果药房已经发药,则护士站发出退费申请,由护士拿着药品去药房退药退费. b.后计 ...

  4. android学习十二 配置变化

    1.配置变化会终止当前活动,并重建活动 2.配置变化有    2.1  屏幕方向变化    2.2  语言变化    2.3   插到基座等   3. 配置变化应用程序不会清除,上下文对新活动依然有效 ...

  5. 初识c++模板元编程

    模板元编程(Template metaprogramming,简称TMP)是编译器内执行的程序,编译器读入template,编译输出的结果再与其他源码一起经过普通编译过程生成目标文件.通俗来说,普通运 ...

  6. katalon系列七:Katalon Studio全局变量

    假如你有3个脚本都用到了用户名,如果是写死在脚本中,那么需要改变的时候,你需要修改3个地方,我们可以把用户名设为全局变量,在3个脚本中引用,需要修改时只要修改全局变量中的用户名值即可. 在Katalo ...

  7. Appium_Python_API说明

    Appium_Python_API 1.contexts contexts(self): Returns the contexts within the current session. 返回当前会话 ...

  8. hibernate.hbm2ddl.auto=update不能自动生成表结构

    在写上篇文章<spring整合springmvc和hibernate>的时候,曾遇到一个问题 INFO: Server startup in 8102 ms Hibernate: inse ...

  9. vuex-Actions的用法

    Action 类似于 mutation,不同在于: Action 提交的是 mutation,而不是直接变更状态. Action 是异步的,mutation是同步的. 沿用vuex学习---简介的案例 ...

  10. 166. Nth to Last Node in List

    Description Find the nth to last element of a singly linked list. The minimum number of nodes in lis ...