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
参考:
[1] https://shiro.apache.org/documentation.html
Shiro 的基本使用的更多相关文章
- shiro权限管理框架与springmvc整合
shiro是apache下的一个项目,和spring security类似,用于用户权限的管理‘ 但从易用性和学习成本上考虑,shiro更具优势,同时shiro支持和很多接口集成 用户及权限管理是众多 ...
- springmvc 多数据源 SSM java redis shiro ehcache 头像裁剪
获取下载地址 QQ 313596790 A 调用摄像头拍照,自定义裁剪编辑头像 B 集成代码生成器 [正反双向](单表.主表.明细表.树形表,开发利器)+快速构建表单; 技术:31359679 ...
- java springMVC SSM 操作日志 4级别联动 文件管理 头像编辑 shiro redis
A 调用摄像头拍照,自定义裁剪编辑头像 B 集成代码生成器 [正反双向](单表.主表.明细表.树形表,开发利器)+快速构建表单; 技术:313596790freemaker模版技术 ,0个代码不用写 ...
- springmvc SSM shiro redis 后台框架 多数据源 代码生成器
A集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单 下载地址 ; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类 ...
- springmvc SSM 多数据源 shiro redis 后台框架 整合
A集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单 下载地址 ; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类 ...
- SpringMVC+Shiro权限管理【转】
1.权限的简单描述 2.实例表结构及内容及POJO 3.Shiro-pom.xml 4.Shiro-web.xml 5.Shiro-MyShiro-权限认证,登录认证层 6.Shiro-applica ...
- shiro的使用2 灵活使用shiro的密码服务模块
shiro最闪亮的四大特征是认证,授权,加密,会话管理. 上一篇已经演示了如何使用shiro的授权模块,有了shiro这个利器,可以以统一的编码方式对用户的登入,登出,认证进行管理,相当的优雅. 为了 ...
- shiro的使用1 简单的认证
最近在重构,有空学了一个简单的安全框架shiro,资料比较少,在百度和google上能搜到的中文我看过了,剩下的时间有空会研究下官网的文章和查看下源码, 简单的分享一些学习过程: 1,简单的一些概念上 ...
- shiro实现session共享
session共享:在多应用系统中,如果使用了负载均衡,用户的请求会被分发到不同的应用中,A应用中的session数据在B应用中是获取不到的,就会带来共享的问题. 假设:用户第一次访问,连接的A服务器 ...
- shiro在springmvc里面的集成使用【转】
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-coll ...
随机推荐
- 文心一言 VS 讯飞星火 VS chatgpt (102)-- 算法导论9.3 8题
八.用go语言,设 X[1..n]和 Y[1..n]为两个数组,每个都包含n个有序的元素.请设计一个 O(lgn)时间的算法来找出数组 X和Y中所有 2n 个元素的中位数. 文心一言: 要在 O(lg ...
- C++算法之旅、08 基础篇 | 质数、约数
质数 在>1的整数中,如果只包含1和本身这两个约数,就被称为质数(素数) 866 试除法判定 866. 试除法判定质数 - AcWing题库 \(O(n)\) bool isprime(int ...
- Go语言代码断行规则详解
本文深入探讨了Go语言中代码断行的各个方面,从基础概念到实际应用实践. 关注[TechLeadCloud],分享互联网架构.云服务技术的全维度知识.作者拥有10+年互联网服务架构.AI产品研发经验.团 ...
- 关于Windows打印机驱动相关问题-如何利用Java(或其他)调用打印机驱动程序完成原始文件翻译为PCL语言的步骤
前面这些都是问题描述,问题在偏下面 场景:用户电脑上安装了PCL驱动,可通过驱动完成打印. 需求:现在需要提供一种脱离PC端完成文件上传并打印的功能.让用户使用手机或pc未安装驱动时都能打印文件. 目 ...
- PTA乙级1039(C++)散列表解法
题目 1039 到底买不买 小红想买些珠子做一串自己喜欢的珠串.卖珠子的摊主有很多串五颜六色的珠串,但是不肯把任何一串拆散了卖. 于是小红要你帮忙判断一下,某串珠子里是否包含了全部自己想要的珠子?如 ...
- include 0。0
参考好文 php://filter的各种过滤器_php://filter过滤器种类-CSDN博客 打开页面是一段php代码 可以知道flag在flag.php文件里面,然后执行没有结果,就只能用文件读 ...
- powerdesigner 生成sql语言
首先要确定的是自己已经准备好一个概念模型 在概念模型界面点击上方工具栏中的Tools->Generate logical data model.. 生成逻辑模型 同样的操作生成物理模型 Gene ...
- 由后缀表达式题目:stoi atoi 函数新发现
洛谷上的题:有些·表示一个操作结束~假装没看到 1 #include<iostream> 2 #include<stack> 3 #include<string> ...
- .NET8.0 AOT 经验分享 - 专项测试各大 ORM 是否支持
AOT 特点 发布和部署本机 AOT 应用具有以下优势: 最大程度减少磁盘占用空间:使用本机 AOT 发布时,将生成一个可执行文件,其中仅包含支持程序所需的外部依赖项的代码.减小的可执行文件大小可能会 ...
- odoo17.0 快递鸟模块
快递鸟是国内使用较为广泛的快递集成查询平台之一,提供了600+的物流公司对接接口,是比较不错的物流查询服务选择.随着odoo17.0的发布,我们最近也将快递鸟模块升级到了17.0.下面我们来详细看一下 ...