Shiro 的基本使用
简介
Apache Shiro 是一个强大的、灵活的开源安全框架,可以干净地处理验证、授权、企业会话管理和加密等功能
相关特性
Apache Shiro 具有的主要特性如下图所示:
主要关注的地方在于 Primary Concerns 这一部分,具体介绍如下:
Authentication(验证):有时也被称为 “登录”Authorization(授权):访问控制,例如:谁能够去做什么Session Management(会话管理):管理用户指定的会话Cryptography(加密):使用加密算法保持数据安全,同时仍然易于使用
概念设计
在最高级别的概念水平上,Shiro 的架构有以下三个关键概念:Subject、SecurityManager 和 Realms。这几个组件之间的交互如下图所示:
Subject(主题):Subject本质上是当前执行的用户的安全特定视图(这里的用户可以是人也可以是其它软件),一个 Subject 可以是一个人,也可以是一个第三方服务。Subject实例都需要绑定到SecurityManager,当你和Subject交互时,这些交互将转换为与Subject指定的SecurityManager进行交互SecurityManager:SecurityManager是 Shiro 架构的核心,SecurityManager充当了一种伞型结构,协调其内部安全组件,共同构成一个对象图。然而,一旦SecurityManager和其内部对象图被一个应用配置了,那么它通常会失效,应用程序的开发者们几乎将他们的时间都花费在了他们的SubjectAPI 上。Realm:Realm作为在 Shiro 和你的应用的安全数据之间充当桥梁(或连接器)的作用,当需要与安全相关的数据(如账户信息)进行实际交互以执行身份验证(登录)和访问控制(授权)时,Shiro 会从应用程序中配置的一个或者多个Realm中查找相关的内容。
Realm 本质上是一个安全特定的 DAO(数据访问对象),它封装了连接到数据源的连接细节,并且使得 Shiro 需要的关联数据变得可用。
当配置 Shiro 时,你必须指定至少一个 Realm 用于身份验证或者授权。SecurityManager 可以配置多个 Realm ,但是至少需要一个 Realm
Shiro 提供了许多的开箱即用的 Realm 来连接到安全数据源(也被称为目录),如 LDAP、关系数据库(JDBC)、文本配置源(如 ini 文件)以及其它。如果这些默认的 Realm 不能呢个满足你的要求,您可以插入你自己的 Realm 实现来表示自定义自定义的安全数据源
具体组件
具体相关组件如下图所示:
-
Subject:org.apache.shiro.subject.Subject简单理解就是当前和系统进行交互的对象
-
SecurityManager:org.apache.shiro.mgt.SecurityManager如上文 “概念设计” 部分提到的,
SecurityManager封装了大部分的功能,是 Shiro 的核心组件。它主要是一个 “伞型” 对象,用于协调其托管组组件以确保它们顺利协同工作。除此之外,SecurityManager还用于管理每个应用程序用户的视图。因此它可以知道如何为每个用户执行安全操作 -
Authenticator:org.apache.shiro.authc.AuthenticatorAuthenticator是负责执行和i响应用户身份验证的(登录)的组件,当一个用户尝试登录时,登录逻辑将会被Authenticator执行。Authenticator知道如何协调一个或多个存储用户(账户)信息的Realm,从这些Realm中获取数据用于验证用户的身份,以确保用户确实是正确的用户。-
Authentication Strategy:org.apache.shiro.authc.pam.AuthenticationStrategy如果超过一个
Realm被配置了,那么AuthenticationStrategy将会协调这些Realm以确定身份验证成功哦你或者失败的条件(例如,如果多个Realm中有一个是成功的,但是其它的Realm都是失败的,那么本次尝试是否是成功的?必须是所有的Realm都成功?还是只需要一个成功即可? )
-
-
Authorizer:org.apache.shiro.authz.AuthorizerAuthorizer组件用于负责用户的访问权限,它是最终决定用户是否被允许做某事的机制。类似Authenticator,Authorizer也知道如何协调多个后端数据源来获取访问角色和权限的信息。Authorizer使用这些信息来确定是否允许用户执行给定的操作 -
SessionManager:org.apache.shiro.session.mgt.SessionManagerSessionManager知道如何创建和管理用户Session的生命周期,以便为所有环境中的用户提供强大的Session体验。在所有的安全框架中,这是 Shiro 特有的一个特征,Shiro 能够在任何环境中本地管理用户会话,即使没有可用的 Web 或 EJB 容器也是如此。默认情况下,Shiro 将会使用现有的会话机制(如 Servlet Container),但是如果没有(例如在独立的应用程序或非 Web 应用程序中),它将使用内置的企业会话管理来提供相同的编程体验
-
SessionDAO:org.apache.shiro.session.mgt.eis.SessionDAOSessionDAO代表SessionManager提供了Session持久化的操作,这允许将任何数据存储插入到会话管理基础架构中。
-
-
CacheManager:org.apache.shiro.cache.CacheManagerCacheManager用于创建和管理其它 Shiro 组件使用的Cache实例的生命周期。由于 Shiro 可以访问许多后端数据源进行身份验证、授权和会话管理,所以缓存一直是框架中的一流架构特性,可以在使用这些数据源的同时提高性能。任何现代的开源或或企业缓存产品都可以插入 Shiro 的缓存中以提高快速高效的用户体验 -
Cryptography:org.apache.shiro.crypto.*加密是企业安全框架的补充。Shiro 加密包下包含了易于使用和理解的加密密码、消息摘要和不同编解码器的实现。这个加密包中的所有类都经过精心设计,非常易于使用和理解。
-
Realm:org.apache.shiro.realm.Realm如 “概念设计” 中提到的,
Realm是应用程序的安全数据和 Shiro 之间进行连接的桥梁
基本使用
对于一般的项目,首先需要将 Shiro 的依赖项加入到你的项目得类路径下,如果是一般的 Maven 项目,需要添加类似如下的依赖项:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.8.0</version> <!-- 具体选择对应的版本 -->
</dependency>
按照 Shiro 官方给出的处理流程,首先会将 Subject 的请求信息发送给 SecurityManager,由 SecurityManager 进行相关的处理。在 SecurityManager 内部,根据不同的功能通过不同的模块进行进一步的处理。
SecurityManager 在 Shiro 中的默认实现是 org.apache.shiro.mgt.DefaultSecurityManager,首先,通过 SecurityUtils 的静态方法设置 SecurityManager:
// 设置当前 Shiro 上下文中的 SecurityManager,默认为 DefaultWebSecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
SecurityUtils.setSecurityManager(defaultSecurityManager);
之后,所有的操作都将围绕 SecurityManager 来进行
用户验证
用户验证分为以下五个步骤,参考上文中的系统组件,具体流程如下图所示:

步骤 1:应用程序代码调用 Subject.login 方法,传入构造的 AuthenticationToken 实例,表示最终用户的principal 和 credential。
步骤 2:Subject 实例,通常是 DelegatingSubject(或子类)通过调用 securityManager.login(token) 委托给应用程序的 SecurityManager,实际身份验证工作从这里开始。
步骤 3:SecurityManager 是一个基本的 “保护伞” 组件,它接收 token 并通过调用authenticator.authenticate(token) 简单地委托给其内部的 Authenticator 实例。 这几乎总是一个 ModularRealmAuthenticator 实例,它支持在身份验证期间协调一个或多个 Realm 实例。 ModularRealmAuthenticator 本质上为 Apache Shiro 提供了 PAM 样式的范例(其中每个领域都是 PAM 术语中的“模块”)
步骤 4:如果为应用程序配置了多个 Realm,那么 ModularRealmAuthenticator 实例将使用其配置的 AuthenticationStrategy 启动多个 Realm 进行身份验证尝试。 在调用 Realm 进行身份验证之前、期间和之后,将调用 AuthenticationStrategy 以允许它对每个 Realm 的结果做出响应。 我们将很快介绍 AuthenticationStrategies。如果只有一个 Realm 被配置了,那么不需要 AuthenticationStrategy 来做额外的工作
步骤 5:每个配置的 Realm 都会被访问,查看是否支持处理提交的 AuthenticationToken。 如果该 Realm 能够处理该 token,那么该 Realm 将会调用自身的 getAuthenticationInfo 方法,并将提交的 token 作为对应的方法参数。 getAuthenticationInfo 方法有效地表示对该特定 Realm 的单一身份验证尝试。
单个 Realm
针对一种特殊的情况,假如现在整个系统中只配置了一个 Realm 用于用户的认证,那么这种情况将会十分简单,具体的使用如下所示:
// 注意:本示例使用的测试环境为 Junit 5
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@SpringBootTest
public class ShiroTest {
static Logger log = LoggerFactory.getLogger(ShiroTest.class);
// Shiro 内置的一个简单的 Realm,通过简单的用户名和密码来进行验证
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
static String USER_NAME = "xhliu";
static String PASSWORD = "123456";
// 在执行正式测试之前添加一个新的账户对象
@BeforeEach
public void addUser() {
simpleAccountRealm.addAccount("xhliu", "123456", "admin", "user");
}
@Test
public void simpleShiroTest() {
// 1. 构建 SecurityManager(核心部分)
DefaultSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager); // 设置当前 Shiro 上下文中的 SecurityManager
// 3. 客户端请求对象
Subject subject = SecurityUtils.getSubject(); // 获取当前 Shiro 上下文环境下的的 客户端请求对象
UsernamePasswordToken token = new UsernamePasswordToken(USER_NAME, PASSWORD); // 通过解析请求得到的用户名和密码构成访问 Token
try {
subject.login(token);
} catch (UnknownAccountException e) {
log.info("用户帐号不存在");
throw e;
} catch (IncorrectCredentialsException e) {
log.info("用户帐号或密码错误");
throw e;
}
log.info("authenticated status: {}", subject.isAuthenticated());
subject.logout(); // 用户退出
log.info("authenticated status: {}", subject.isAuthenticated());
}
}
执行测试,会得到类似如下的输出结果:

这是一种比较简单的情况,但是如果此时定义了两个不同的 Realm,比如 SimpleAccountRealm 和 JdbcRealm,但是只有其中一个 Realm 是匹配成功的,那么应该怎么办?这种情况下,SecurityManager 中的 AuthenticationStrategy 就要派上用场了
多个 Realm
AuthenticationStrategy 是一个无状态组件,在身份验证期间被查询 4 次(这 4 次交互所需的任何必要状态都将作为方法参数给出):
- 在任意的
Realm被调用之前 - 在调用单个
Realm的getAuthenticationInfo方法之前 - 在调用单个
Realm的getAuthenticationInfo方法之后 - 在调用了所有
Realm之后
AuthenticationStrategy 负责聚合来自每个验证成功的 Realm 的结果,并将它们 “捆绑” 成单个 AuthenticationInfo 。 这个最终聚合的 AuthenticationInfo 实例是 Authenticator 实例返回的内容,也是 Shiro 用来表示 Subject 的最终身份(又名 Principals)的内容
如果在应用程序中使用多个 Realm 从多个数据源获取帐户数据,那么 AuthenticationStrategy 负责生成应用程序看到的 Subject 身份的 “合并” 视图
AuthenticationStrategy 有三个具体的实现,如下表所示:
AuthenticationStrategy 类 |
描述 |
|---|---|
AtLeastOneSuccessfulStrategy |
如果一个(或多个)Realm 验证成功,则整体被认为是成功的。 如果没有一个验证成功,则认为是失败的。 |
FirstSuccessfulStrategy |
只会得到从第一个成功认证的 Realm 返回的信息。 其他的 Realm 将被忽略。 如果没有成功验证,则尝试失败。 |
AllSuccessfulStrategy |
所有配置的 Realm 都必须成功地进行身份验证,整个尝试才能被视为成功。 如果任何一个未成功验证,则整体视为失败。 |
前文提到,SecurityManager 默认的 Authenticato 是 ModularRealmAuthenticator,而 ModularRealmAuthenticator 的默认 AuthenticationStrategy 实现类是 AtLeastOneSuccessfulStrategy
当然,也可以通过在 shiro.ini 配置文件中进行修改,也可以通过程序化的方式来完成:
在 shiro.ini 配置文件中进行配置:
# 将 SecurityManager 的默认认证策略设置为 FirstSuccessfulStrategy
authStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authStrategy
此时通过该 shiro.ini 文件加载对应的配置,即可完成对应的配置
通过程序化的方式进行配置:
DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
// ModularRealmAuthenticator 是默认的 Authenticator
ModularRealmAuthenticator authenticator = (ModularRealmAuthenticator) securityManager.getAuthenticator();
// 设置存在多个 Realm 时的认证策略
authenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
securityManager.setAuthenticator(authenticator);
用户授权
类似认证在 SecurityManager 中的顺序,授权在 SecurityManager 中的执行顺序如下图所示:

步骤 1:应用程序或框架代码调用任何 Subject 的 hasRole*、checkRole*、isPermitted* 或 checkPermission* 方法变体,传入所需的任何权限或角色表示。
步骤 2:Subject 实例,通常是 DelegatingSubject(或子类),通过调用 securityManager 的几乎相同的相应 hasRole*、checkRole*、isPermitted* 或 checkPermission* 方法变体,委托给应用程序的 SecurityManager(securityManager 实现了 org.apache.shiro.authz.Authorizer 接口,它定义了所有特定于 Subject 的授权方法)
步骤 3:SecurityManager 是一个基本的“保护伞”组件,通过调用授权方各自的 hasRole*、checkRole*、isPermitted* 或 checkPermission* 方法来中继/委托到其内部 org.apache.shiro.authz.Authorizer 实例。 授权器实例默认是一个 ModularRealmAuthorizer 实例,它支持在任何授权操作期间协调一个或多个 Realm 实例。
步骤 4:检查每个配置的 Realm 以查看它是否实现了相同的 Authorizer 接口。 如果是,则调用 Realm 各自的 hasRole*、checkRole*、isPermitted* 或 checkPermission* 方法。
Shiro 提供了三种方式来给指定的 Subject 进行授权:
- 程序化 — 您可以使用
if和else块等结构在您的 java 代码中执行授权检查。 - JDK 注解 — 您可以将授权注解附加到您的 Java 方法
- JSP/GSP TagLibs — 您可以根据角色和权限控制 JSP 或 GSP 页面输出(本文不做介绍)
在介绍这几种方式之前,首先来介绍一下 Shiro 对于授权的处理方
基于角色的授权
如果您只想检查当前 Subject 是否有角色,您可以在 Subject 实例上调用变体 hasRole* 方法。
例如,要查看 Subject 是否具有特定(单一)的角色,您可以调用 subject.hasRole(roleName) 方法,并做出相应的响应:
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
//show the admin button
} else {
//don't show the button? Grey it out?
}
这种授权方式看起来比较正常,是将权限授予给角色,使得该用户具有该角色来判断是否具有对应的权限。这么做理论上来讲是合理的,但是在这个例子中,已经将角色和权限耦合到一起去了,因此目前 Shiro 的推荐做法是使用基于权限的授权而不是基于角色的授权。
RBAC: Role-Based Access Controller,现在应该转换为 Resource-Based Access Controller,具体可以看看 The New RBAC: Resource-Based Access Control
基于权限的授权
基于权限的授权是被推荐使用的,Shiro 提供了两种方式来进行权限的校验:
-
基于
Permission的权限检查执行权限检查的一种方法是实例化 Shiro 的
org.apache.shiro.authz.Permission接口的实例,并将其传递给接受权限实例的*isPermitted方法例如,考虑如下假设:办公室中有一台打印机,其唯一标识符为
laserjet4400n,在允许当前用户按下“打印”按钮之前,软件需要检查是否允许当前用户在该打印机上打印文档。权限检查可以这样表述:Permission printPermission = new PrinterPermission("laserjet4400n", "print"); Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isPermitted(printPermission)) { //show the Print button } else { //don't show the button? Grey it out? }这种方式的优点在于表达的权限十分清晰,能够正确表现其意图;缺点在于需要写更多的代码
-
基于字符串内容的权限检查
上面的例子转换为对应的字符串权限检查如下所示:
Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isPermitted("printer:print:laserjet4400n")) { //show the Print button } else { //don't show the button? Grey it out? }这种方式在很多场景下能够减少代码量,因此大部分情况下都会采用这种方式来进行权限检查。
这种方式最终是通过
org.apache.shiro.authz.permission.WildcardPermission来完成每个部分的分离的,因此,这就相当于:Subject currentUser = SecurityUtils.getSubject(); Permission p = new WildcardPermission("printer:print:laserjet4400n"); if (currentUser.isPermitted(p) { //show the Print button } else { //don't show the button? Grey it out? }
程序化的授权
如上面例子,就是一般的通过写代码的方式来完成授权的工作。具体的相关 API 请参考:https://shiro.apache.org/static/1.8.0/apidocs/
注解式的授权
注解式的授权基于 AOP ,因此,在使用注解的方式来完成授权工作时,需要开启 AOP
注解式的授权主要有以下几种注解:
-
RequiresAuthentication:RequiresAuthentication注解要求当前Subject在其当前会话期间已通过身份验证,以便访问或调用注解的类/实例/方法。@RequiresAuthentication public void updateAccount(Account userAccount) { // 进入该方法之前需要当前的账户已经通过认证 } -
RequiresGuest:RequiresGuest注解要求当前的Subject是一个“guest”,也就是说,它们没有被认证或从先前的会话中被记住,以便访问或调用注解的类/实例/方法@RequiresGuest public void signUp(User newUser) { // 进入该方法之前需要该账户没有被认证或“记住” } -
RequiresPermissions:RequiresPermissions注解要求当前Subject被授予一个或多个权限,以便执行注解的方法@RequiresPermissions("account:create") public void createAccount(Account account) { // 进入该方法体之前要求账户具有创建账户的权限 } -
RequiresRoles:RequiresRoles注释要求当前Subject具有所有指定的角色。 如果他们没有角色,则不会执行该方法并抛出AuthorizationException@RequiresRoles("administrator") public void deleteUser(User user) { /// 进入该方法体之前要求该用户的角色是 administrato } -
RequiresUser:RequiresUser*注释要求当前Subject是应用程序用户,以便访问或调用带注释的类/实例/方法。应用程序用户” 被定义为具有已知身份的
Subject,该身份是由于在当前会话期间通过身份验证而已知的,或者是从先前会话的 “RememberMe” 服务中记住的@RequiresUser public void updateAccount(Account account) { // 只有当当前的 Subject 用户是据有身份时才能执行 }
整合到 Spring
首先,添加对应的依赖项到本地 Spring 项目,这里以 Maven 项目为例:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version> <!-- 具体对应相关的版本-->
</dependency>
本部分中的所有配置都是通过配置类的方式来配置的,基于 SpringBoot 2.6.2,Shiro 中也存在对应的 start 依赖项:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version> <!-- 具体对应相关的版本-->
</dependency>
Hint:Shiro 集成的 spring-boot-start 存在些许问题,可能会使得 Spring 中正常地组件类出现异常,因此不建议直接使用 shiro 的集成 start
配置 Shiro
首先,在 SpringBoot 的主应用程序的目录创建一个 config 目录,用于创建配置类
在新建的 config 目录下创建一个 ShiroConfig 的配置类,用于加载 Shiro 需要的配置,具体内容如下:
import org.apache.shiro.spring.config.ShiroAnnotationProcessorConfiguration;
import org.apache.shiro.spring.config.ShiroBeanConfiguration;
import org.apache.shiro.spring.config.ShiroConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({ // 通过导入需要的相关的 Class,可以将 Shiro 中必须的 Bean 加载到 Spring 容器中
ShiroBeanConfiguration.class, // 配置 Shiro 生命周期和事件
ShiroConfiguration.class, // 配置 Shiro Beans,如:SecurityManager、SessionManager 等等
ShiroAnnotationProcessorConfiguration.class // 开启注解的配置方式
})
public class ShiroConfig {
// 注意,在加入这些配置 Bean 之前,请确保整个 Spring 容器中至少存在一个 Realm
}
在 Web 应用中进行配置
新建一个 ShiroWebConfig 的配置类,用于配置和 Web 应用相关的特有配置:
package org.xhliu.demo.config;
import org.apache.shiro.spring.config.ShiroAnnotationProcessorConfiguration;
import org.apache.shiro.spring.config.ShiroBeanConfiguration;
import org.apache.shiro.spring.web.config.ShiroRequestMappingConfig;
import org.apache.shiro.spring.web.config.ShiroWebConfiguration;
import org.apache.shiro.spring.web.config.ShiroWebFilterConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({
ShiroBeanConfiguration.class, // 配置 Shiro 的生命周期和事件
ShiroAnnotationProcessorConfiguration.class, // 开启 Shiro 的注解处理
ShiroWebConfiguration.class, // 这个配置类会加载必要的配置 Bean,如默认的 SecurityManager 和 SessionManager 等
ShiroWebFilterConfiguration.class, // 配置 Web 应用的过滤器
ShiroRequestMappingConfig.class // 使用 Shiro 对于 Spring 框架的 `UrlPathHelper` 的实现,确保 Shiro 的处理和 Spring 对于 Web 的处理相同
})
public class ShiroWebConfig {
// 同样的,在整个系统中至少需要存在一个 Realm 用于实际的用户检测
}
在导入相关的配置类之后,需要定义一个 ShiroFilterChainDefinition 来定义访问路径的处理:
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
/*
注意:对于每个请求,都会按照当前的配置顺序进行检查,因此一定要将进行检查的请求路径放在前面一些配置,
否则,某些配置,如 "/**" 设置为匿名访问并放在第一位,将会使得整个过滤链功能失效
*/
// 进入到 /admin 路径的请求必须具有 'admin' 的角色
chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]");
// 进入到 /docs 路径的用户必须具有读取权限
chainDefinition.addPathDefinition("/docs/**", "authc, perms[document:read]");
// 所有 /user 的请求都必须经过认证
chainDefinition.addPathDefinition("/user", "authc");
// "/view" 的请求也必须是经过认证的
chainDefinition.addPathDefinition("/view", "authc");
// 对于 /tmp 的请求可以是匿名的(即不需要经过认证)
chainDefinition.addPathDefinition("/**", "anon");
return chainDefinition;
}
其中,每个方法调用的第二个参数都是有 Shiro 内置的定义信息,具体细节如下所示:
| Filter Name | Class |
|---|---|
| anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
| authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
| authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
| perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
| port | org.apache.shiro.web.filter.authz.PortFilter |
| rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
| roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
| ssl | org.apache.shiro.web.filter.authz.SslFilter |
| user | org.apache.shiro.web.filter.authc.UserFilter |
以上的配置方式不是特别直观,因此 Shiro 也提供了注解的方式来进行显式的配置,具体如下所示:
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class AdminController {
@RequiresRoles("admin") // 对于当前的请求,需要具有 admin 的角色
@RequestMapping(path = "/admin/config")
public String admin() {
return "view";
}
}
全局配置
现在将相关的配置信息放入到一个 Bean 中,可以简化许多的内容,
对应 org.apache.shiro.spring.web.ShiroFilterFactoryBean 类型的 Bean,具体如下所示:
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 配置 SecurityManager,这里的 SecurityManager 是通过注入来实现的,因为我们已经将 ShiroConfiguration 加载到 Spring 容器中了,ShiroConfiguration 中定义了 SecurityManager Bean
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 过滤器,对应上文中的 ShiroFilterChainDefinition,但是在这里是通过 Map 的形式来定义的
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
// 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
// 过滤链定义,从上向下顺序执行,一般将/** 放在最为下边
filterChainDefinitionMap.put("/**", "authc");
// 设置登录的请求路径,默认为 "/login.jsp"
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的路径
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授权界面的重定向访问路径
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
更多的配置细节如下表所示:
shiro.sessionManager.deleteInvalidSessions |
true |
从会话存储中删除无效会话 |
|---|---|---|
shiro.sessionManager.sessionIdCookieEnabled |
true |
启用会话 ID 到 cookie,用于会话跟踪 |
shiro.sessionManager.sessionIdUrlRewritingEnabled |
true |
启用会话 URL 重写支持 |
shiro.userNativeSessionManager |
false |
如果启用 Shiro 将管理 HTTP 会话而不是容器 |
shiro.sessionManager.cookie.name |
JSESSIONID |
会话 cookie 名称 |
shiro.sessionManager.cookie.maxAge |
-1 |
会话 cookie 最大存活时间 |
shiro.sessionManager.cookie.domain |
null | 会话 cookie 域 |
shiro.sessionManager.cookie.path |
null | 会话 cookie 路径 |
shiro.sessionManager.cookie.secure |
false |
会话 cookie 安全标志 |
shiro.rememberMeManager.cookie.name |
rememberMe |
“记住我” 的 cookie 名称 |
shiro.rememberMeManager.cookie.maxAge |
one year | ”记住我“ 的 cookie 最大存活时间 |
shiro.rememberMeManager.cookie.domain |
null | “记住我” 的 cookie 域 |
shiro.rememberMeManager.cookie.path |
null | ”记住我“ 的 cookie 路径 |
shiro.rememberMeManager.cookie.secure |
false |
”记住我“ 的 cookie 安全标记 |
shiro.loginUrl |
/login.jsp |
将未经身份验证的用户重定向到登录页面时使用的登录 URL |
shiro.successUrl |
/ |
用户登录后的默认登录页面(如果在当前会话中找不到替代方案) |
shiro.unauthorizedUrl |
null | 如果用户未经授权,则将其重定向到的页面(403 页面) |
具体的项目请参考:https://github.com/LiuXianghai-coder/Spring-Study/tree/master/spring-shiro
参考:

浙公网安备 33010602011771号