目录
1、关于Shiro
官网介绍
Shiro特点
Shiro基本功能点
Shiro架构
2、快速入门
2.1、Shiro身份验证
2.2、Shiro授权
1、关于Shiro
官网介绍
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
Apache Shiro™是一个强大且易用的 Java 安全框架, 能够用于身份验证、授权、加密和会话管理。Shiro 拥有易于理解的 API, 您可以快速、轻松地获得任何应用程序——从最小的移动应用程序到最大的网络和企业应用程序。
简而言之,Apache Shiro 是一个强大灵活的开源安全框架,可以完全处理身份验证、授权、加密和会话管理。
Shiro特点
- 易于使用——易用性是项目的最终目标。应用程序安全非常令人困惑和沮丧, 被认为是 “不可避免的灾难”。如果你让它简化到新手都可以使用它, 它就将不再是一种痛苦了。
- 全面——没有其他安全框架的宽度范围可以同 Apache Shiro 一样, 它可以成为你的 “一站式” 为您的安全需求提供保障。
- 灵活——Apache Shiro 可以在任何应用程序环境中工作。虽然在网络工作、EJB 和 IoC 环境中可能并不需要它。但 Shiro 的授权也没有任何规范, 甚至没有许多依赖关系。
- Web 支持——Apache Shiro 拥有令人兴奋的 web 应用程序支持, 允许您基于应用程序的 url 创建灵活的安全策略和网络协议 (例如 REST), 同时还提供一组 JSP 库控制页面输出。
- 低耦合——Shiro 干净的 API 和设计模式使它容易与许多其他框架和应用程序集成。你会看到 Shiro 无缝地集成 Spring 这样的框架, 以及 Grails, Wicket, Tapestry, Mule, Apache Camel, Vaadin... 等。
- 被广泛支持——Apache Shiro 是 Apache 软件基金会的一部分。项目开发和用户组都有友好的网民愿意帮助。这样的商业公司如果需要 Katasoft 还提供专业的支持和服务。
Shiro基本功能点
- Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
- Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
- Web Support:Web 支持,可以非常容易的集成到 Web 环境;
- Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;
- Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
- Testing:提供测试支持;
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
- Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。
Shiro架构
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager 和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。

- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
- SecurityManager:管理所有 Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到 Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个 Realm 来实现认证(authentication)和 / 或授权(authorization)。
我们需要实现 Realms 的 Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
2、快速入门
2.1、Shiro身份验证
shiro认证流程图
示例代码:
1、创建maven工程,引入相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2、创建测试类
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class AuthenticationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
// 在方法开始前添加一个用户
@Before
public void addUser() {
simpleAccountRealm.addAccount("admin", "111111");
}
@Test
public void testAuthentication() {
// 1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager); // 设置SecurityManager环境
Subject subject = SecurityUtils.getSubject(); // 获取当前主体
UsernamePasswordToken token = new UsernamePasswordToken("admin", "111111");
subject.login(token); // 登录
// subject.isAuthenticated()方法返回一个boolean值,用于判断用户是否认证成功
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 输出true
subject.logout(); // 登出
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 输出false
}
}
3、运行并查看结果
运行之后可以看到isAuthenticated先打印true,说明认证成功;第二次打印为false,是因为用户已登出,认证失败。
具体的身份验证流程:
流程如下:
- 首先调用
进行登录,其会自动委托给
Subject.login(token)
,调用之前必须通过
Security Manager
设置;
SecurityUtils.setSecurityManager()
负责真正的身份验证逻辑;它会委托给
SecurityManager
进行身份验证;
Authenticator
才是真正的身份验证者,
Authenticator
中核心的身份认证入口点,此处可以自定义插入自己的实现;
Shiro API
可能会委托给相应的
Authenticator
进行多
AuthenticationStrategy
身份验证,默认
Realm
会调用
ModularRealmAuthenticator
进行多
AuthenticationStrategy
身份验证;
Realm
会把相应的
Authenticator
传入
token
,从
Realm
获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个
Realm
,将按照相应的顺序及策略进行访问。
Realm
2.2、Shiro授权
授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
主体
主体,即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
资源
在应用中用户可以访问的URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
权限
安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面
查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)打印文档等。如上可以看出,权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。
角色
角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
隐式角色:
即直接通过角色来验证用户有没有操作权限,如在应用中 CTO、技术总监、开发工程师可以使用打印机,假设某天不允许开发工程师使用打印机,此时需要从应用中删除相应代码;再如在应用中 CTO、技术总监可以查看用户、查看权限;突然有一天不允许技术总监查看用户、查看权限了,需要在相关代码中把技术总监角色从判断逻辑中删除掉;即粒度是以角色为单位进行访问控制的,粒度较粗;如果进行修改可能造成多处代码修改。
显式角色:
在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设哪个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无须修改多处代码;即粒度是以资源/实例为单位的;粒度较细。
shiro授权流程图
示例代码:
1、引入相关依赖,参考2.1节
2、创建测试类
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class AuthorizationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before // 在方法开始前添加一个用户,让它具备admin和user两个角色
public void addUser() {
simpleAccountRealm.addAccount("test", "111111", "admin", "user");
}
@Test
public void testAuthorization() {
// 1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager); // 设置SecurityManager环境
Subject subject = SecurityUtils.getSubject(); // 获取当前主体
UsernamePasswordToken token = new UsernamePasswordToken("test", "111111");
subject.login(token); // 登录
// subject.isAuthenticated()方法返回一个boolean值,用于判断用户是否认证成功
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 输出true
// 判断subject是否具有admin和user两个角色权限,如没有则会报错
subject.checkRoles("admin","user");
System.out.println("用户具有admin权限: "+subject.hasRole("admin"));
System.out.println("用户具有guest权限: "+subject.hasRole("guest"));
}
}
3、运行并查看结果
具体的授权流程:
流程如下:
- 首先调用
接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer;
Subject.isPermitted*/hasRole*
- Authorizer 是真正的授权者,如果我们调用如 isPermitted(“user:view”),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例;
- 在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;
- Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个 Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如
会返回 true,否则返回 false 表示授权失败。
isPermitted*/hasRole*
ModularRealmAuthorizer 进行多 Realm 匹配流程:
- 首先检查相应的 Realm 是否实现了实现了 Authorizer;
- 如果实现了 Authorizer,那么接着调用其相应的
接口进行匹配;
isPermitted*/hasRole*
- 如果有一个 Realm 匹配那么将返回 true,否则返回 false。
如果 Realm 进行授权的话,应该继承 AuthorizingRealm,其流程是:
- 如果调用
,则直接获取 AuthorizationInfo.getRoles() 与传入的角色比较即可;首先如果调用如 isPermitted(“user:view”),首先通过 PermissionResolver 将权限字符串转换成相应的 Permission 实例,默认使用 WildcardPermissionResolver,即转换为通配符的 WildcardPermission;
hasRole*
- 通过 AuthorizationInfo.getObjectPermissions() 得到 Permission 实例集合;通过 AuthorizationInfo.getStringPermissions() 得到字符串集合并通过 PermissionResolver 解析为 Permission 实例;然后获取用户的角色,并通过 RolePermissionResolver 解析角色对应的权限集合(默认没有实现,可以自己提供);
- 接着调用 Permission.implies(Permission p) 逐个与传入的权限比较,如果有匹配的则返回 true,否则 false。