Apache Shiro 安全框架详解:从入门到实战 🛡️

Apache Shiro 是一个强大且易用的 Java 安全框架,提供认证、授权、加密、会话管理等完整的安全功能。相比 Spring Security,Shiro 更加轻量、配置更加简洁,学习曲线更平缓。本文将带你全面理解 Shiro 的核心概念、使用方法以及实战应用!💪


📚 目录导航


一、Shiro 概述:为什么选择 Shiro?

1.1 Shiro 简介

Apache Shiro 是 Apache 软件基金会的顶级项目,前身为 JSecurity(2004年)。它是一个功能强大且易于使用的 Java 安全框架,旨在简化应用程序的安全管理工作。

Shiro 的设计理念:

“Apache Shiro 提供了一套完整的安全管理功能,任何应用都可以轻松获得强大的安全能力,而无需依赖容器特定的配置。”

1.2 Shiro vs Spring Security 对比

对比维度 Shiro Spring Security
学习曲线 平缓,易上手 陡峭
配置复杂度 简单,INI 配置 复杂,Java 配置
依赖 独立,依赖少 强依赖 Spring
社区活跃度 一般 非常活跃
文档 英文为主 中文资料丰富
适用场景 轻量级应用、微服务 企业级应用、Spring 全家桶
与 Spring Boot 集成 shiro-spring-boot-web-starter spring-boot-starter-security

1.3 Shiro 核心功能

1.4 Shiro 特性一览

特性 说明
易用性 API 简洁直观,上手快
独立性 不依赖容器,可独立运行
灵活性 可在任意 Java 程序中使用
Web 支持 强大的 URL 过滤和拦截
可扩展 自定义组件易于替换
Session 独立于容器的会话管理
缓存 支持 Ehcache、Redis 等缓存
测试 支持 JUnit、TestNG 等测试框架

二、Shiro 核心架构与概念

2.1 Shiro 架构图

Shiro 的核心架构由三个主要部分组成:

2.2 核心组件详解

1️⃣ Subject(主体)

Subject 是 Shiro 的核心概念之一,代表当前与程序交互的实体(用户、第三方服务等)。

1
2
3
4
5
6
7
8
9
// 获取当前登录用户
Subject currentUser = SecurityUtils.getSubject();

// 常用操作
if (currentUser.isAuthenticated()) {
// 用户已认证
currentUser.getPrincipal(); // 获取身份信息
currentUser.getSession(); // 获取会话
}

2️⃣ SecurityManager(安全管理器)

SecurityManager 是 Shiro 的核心管理器,协调所有安全相关的组件

3️⃣ Realm(领域)

Realm 是连接 Shiro 和程序数据(如数据库)的桥梁。当进行认证和授权时,Shiro 会从 Realm 中获取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 自定义 Realm 示例
*/
public class CustomRealm extends AuthorizingRealm {

// 认证方法:验证用户身份
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {

UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();

// 从数据库查询用户信息
User user = userDao.findByUsername(username);

if (user == null) {
throw new UnknownAccountException("用户不存在");
}

// 返回认证信息
return new SimpleAuthenticationInfo(
user.getUsername(), // 身份(Principal)
user.getPassword(), // 凭证(Credential)
getName() // Realm 名称
);
}

// 授权方法:获取用户权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {

String username = (String) principals.getPrimaryPrincipal();

// 从数据库查询角色和权限
Set<String> roles = roleDao.findRolesByUsername(username);
Set<String> permissions = permissionDao.findPermissionsByUsername(username);

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roles);
info.setStringPermissions(permissions);

return info;
}
}

2.3 Shiro 认证与授权流程


三、快速入门:五分钟跑通 Shiro

3.1 添加 Maven 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- Shiro 核心 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.13.0</version>
</dependency>

<!-- Shiro Web 支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.13.0</version>
</dependency>

<!-- Shiro Spring Boot 集成 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.13.0</version>
</dependency>

<!-- Shiro 缓存支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.13.0</version>
</dependency>

3.2 Shiro INI 配置(经典方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
; shiro.ini - Shiro 经典配置文件

[main]
; 定义密码加密器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=MD5
credentialsMatcher.hashIterations=2
credentialsMatcher.storedCredentialsHexEncoded=true

; 定义 Realm
customRealm=com.example.realm.CustomRealm
customRealm.credentialsMatcher=$credentialsMatcher

; 定义缓存管理器
cacheManager=org.apache.shiro.cache.MemoryConstrainedCacheManager
customRealm.cacheManager=$cacheManager

; 配置 SecurityManager
securityManager.realms=$customRealm
securityManager.cacheManager=$cacheManager

[users]
; 用户名=密码,角色列表
admin=admin123,admin,user
test=test123,user

[roles]
; 角色=权限列表
admin=*
user=article:read,article:write

[urls]
; URL=过滤器链
/login=anon
/static/**=anon
/logout=logout
/admin/**=authc,roles[admin]
/user/**=authc
/**=authc

3.3 Shiro Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* Shiro Hello World 示例
*/
public class ShiroHelloWorld {

public static void main(String[] args) {
// 1. 加载 INI 配置
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();

// 2. 设置 SecurityManager(全局)
SecurityUtils.setSecurityManager(securityManager);

// 3. 获取当前用户
Subject currentUser = SecurityUtils.getSubject();

// 4. 执行登录
UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin123");
try {
currentUser.login(token);
System.out.println("✅ 登录成功!");

// 5. 检查角色
if (currentUser.hasRole("admin")) {
System.out.println("👤 用户是管理员");
}

// 6. 检查权限
if (currentUser.isPermitted("article:write")) {
System.out.println("✍️ 用户可以写文章");
}

} catch (UnknownAccountException e) {
System.out.println("❌ 用户不存在");
} catch (IncorrectCredentialsException e) {
System.out.println("❌ 密码错误");
} catch (AuthenticationException e) {
System.out.println("❌ 认证失败:" + e.getMessage());
} finally {
// 7. 登出
currentUser.logout();
}
}
}

四、Shiro 认证流程详解

4.1 AuthenticationToken 详解

AuthenticationToken 是用户身份凭证的抽象,Shiro 使用它来携带认证信息:

1
2
3
4
5
6
7
8
9
10
11
/**
* 最常用的 UsernamePasswordToken
*/
UsernamePasswordToken token = new UsernamePasswordToken(
"username", // 用户名
"password" // 密码
);

// 可选参数
token.setRememberMe(true); // 记住我
token.setHost("192.168.1.1"); // 登录主机地址

4.2 AuthenticationInfo 详解

AuthenticationInfo 是认证信息的抽象,包含三个核心元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* SimpleAuthenticationInfo 示例
*/
public class CustomRealm extends AuthorizingRealm {

@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {

UsernamePasswordToken loginToken = (UsernamePasswordToken) token;
String username = loginToken.getUsername();

// 查询用户
User user = userDao.findByUsername(username);

if (user == null) {
throw new UnknownAccountException("用户不存在");
}

// 返回 SimpleAuthenticationInfo
return new SimpleAuthenticationInfo(
user.getUsername(), // Principal:身份标识(通常是用户名)
user.getPassword(), // Credential:凭证(通常是加密后的密码)
ByteSource.Util.bytes(user.getSalt()), //盐值,用于密码比对
getName() // Realm 名称
);
}
}

4.3 密码加密与比对

Shiro 支持多种密码加密算法,推荐使用 Hash + Salt 方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 配置密码加密器
*/
@Bean
public CredentialsMatcher credentialsMatcher() {
// 配置 HashedCredentialsMatcher
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("SHA-256"); // 加密算法
matcher.setHashIterations(1024); // 迭代次数
matcher.setStoredCredentialsHexEncoded(false); // Hex 编码

return matcher;
}

@Bean
public CustomRealm customRealm() {
CustomRealm realm = new CustomRealm();
realm.setCredentialsMatcher(credentialsMatcher());
return realm;
}

/**
* 密码加密示例
*/
public class PasswordUtil {

/**
* 生成密码哈希
*/
public String encryptPassword(String rawPassword, String salt) {
SimpleHash hash = new SimpleHash(
"SHA-256", // 算法
rawPassword, // 明文密码
salt, // 盐值
1024 // 迭代次数
);
return hash.toHex(); // 返回十六进制字符串
}

/**
* 生成随机盐值
*/
public String generateSalt() {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
}

4.4 多种认证方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* 多种认证方式实现
*/

// 1. 用户名密码认证(最常用)
UsernamePasswordToken token = new UsernamePasswordToken(username, password);

// 2. 表单认证(Web 应用)
UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);

// 3. JWT Token 认证(自定义 Token)
public class JwtToken implements AuthenticationToken {

private String token;

public JwtToken(String token) {
this.token = token;
}

@Override
public Object getPrincipal() {
return token;
}

@Override
public Object getCredentials() {
return token;
}
}

// 4. 自定义认证(手机号验证码)
public class SmsToken implements AuthenticationToken {

private String phoneNumber;
private String smsCode;

public SmsToken(String phoneNumber, String smsCode) {
this.phoneNumber = phoneNumber;
this.smsCode = smsCode;
}

@Override
public Object getPrincipal() {
return phoneNumber;
}

@Override
public Object getCredentials() {
return smsCode;
}
}

五、授权与权限控制

5.1 授权的三种方式

1️⃣ 编程式授权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Subject currentUser = SecurityUtils.getSubject();

// 检查角色
if (currentUser.hasRole("admin")) {
// 有管理员角色
}

// 检查多个角色(需要同时具备)
if (currentUser.hasAllRoles(Arrays.asList("admin", "user"))) {
// 同时具备 admin 和 user 角色
}

// 检查权限
if (currentUser.isPermitted("article:write")) {
// 有写文章权限
}

// 检查多个权限(只需具备其一)
if (currentUser.isPermitted("article:write", "article:edit")) {
// 有写或编辑文章权限
}

// 批量检查权限
if (currentUser.isPermitted("article:*")) {
// 有文章相关的所有权限
}

2️⃣ 注解式授权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 使用注解进行授权控制
*/
@Service
public class UserService {

// 需要 ADMIN 角色
@RequiresRoles("admin")
public void deleteUser(Long userId) {
userRepository.deleteById(userId);
}

// 需要多个角色之一
@RequiresRoles(value = {"admin", "manager"}, logical = Logical.OR)
public void exportData() {
// 导出数据
}

// 需要特定权限
@RequiresPermissions("user:create")
public User createUser(User user) {
return userRepository.save(user);
}

// 需要多个权限
@RequiresPermissions(value = {"user:update", "user:read"})
public User updateUser(User user) {
return userRepository.update(user);
}

// 允许任何人访问
@RequiresGuest
public void register(User user) {
// 注册逻辑
}

// 需要已认证用户
@RequiresAuthentication
public void getProfile() {
// 获取用户资料
}
}

3️⃣ JSP 标签式授权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- 在 JSP 页面中使用 Shiro 标签 -->
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

<html>
<body>

<!-- 显示已认证用户 -->
<shiro:authenticated>
欢迎,<shiro:principal/>!
<a href="/logout">退出</a>
</shiro:authenticated>

<!-- 显示未认证用户 -->
<shiro:notAuthenticated>
<a href="/login">登录</a>
</shiro:notAuthenticated>

<!-- 角色检查 -->
<shiro:hasRole name="admin">
<a href="/admin">管理后台</a>
</shiro:hasRole>

<!-- 权限检查 -->
<shiro:hasPermission name="article:write">
<a href="/article/write">写文章</a>
</shiro:hasPermission>

<!-- 多个角色检查 -->
<shiro:hasAnyRoles name="admin,manager">
您是管理员或经理
</shiro:hasAnyRoles>

</body>
</html>

5.2 Shiro 内置注解说明

注解 说明 示例
@RequiresAuthentication 需要已认证用户 @RequiresAuthentication
@RequiresUser 需要已认证或记住我用户 @RequiresUser
@RequiresGuest 需要未认证的访客 @RequiresGuest
@RequiresRoles 需要特定角色 @RequiresRoles("admin")
@RequiresPermissions 需要特定权限 @RequiresPermissions("user:create")
@RequiresServer 需要特定服务器 @RequiresServer

5.3 权限的通配符规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Shiro 权限通配符规则
*/

// 精确权限
"user:create" // 创建用户权限

// 多值权限
"user:create,read,update" // 多个权限

// 所有操作
"user:*" // 用户相关的所有权限

// 所有用户
"*:*" // 所有权限

// 系统级别权限
"system:user:manage" // 系统用户管理

六、Shiro 内置过滤器

6.1 过滤器配置

Shiro 通过过滤器链来拦截请求并进行安全控制:

1
2
3
4
5
6
7
8
[urls]
; 格式:URL = 过滤器链
/login = anon ; 匿名访问
/logout = logout ; 登出
/static/** = anon ; 静态资源匿名访问
/admin/** = authc, roles[admin] ; 需要认证且有 admin 角色
/user/** = authc ; 需要认证
/** = anon ; 其他全部匿名访问

6.2 常用过滤器一览

6.3 过滤器配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[urls]
; 1. 匿名过滤器 - 任何人都可以访问
/login = anon
/register = anon
/captcha = anon

; 2. 认证过滤器 - 必须登录才能访问
/home = authc
/profile = authc

; 3. 角色过滤器 - 必须拥有特定角色
/admin/** = authc, roles[admin]
/manager/** = authc, roles[admin, manager]

; 4. 权限过滤器 - 必须拥有特定权限
/user/create = authc, perms[user:create]
/user/delete = authc, perms[user:delete]

; 5. HTTP Basic 认证
/api/** = authcBasic

; 6. SSL 强制跳转
/payment/** = ssl[443]

; 7. 组合过滤器
/dashboard = authc, roles[user], perms[dashboard:read]

6.4 自定义过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 自定义过滤器示例:IP 白名单过滤器
*/
public class Ip whitelistFilter extends AccessControlFilter {

private Set<String> allowedIPs = new HashSet<>();

public void setAllowedIPs(String ips) {
this.allowedIPs = Arrays.asList(ips.split(","));
}

@Override
protected boolean isAccessAllowed(ServletRequest request,
ServletResponse response,
Object mappedValue) throws Exception {

String clientIp = getClientIp(request);
return allowedIPs.contains(clientIp);
}

@Override
protected boolean onAccessDenied(ServletRequest request,
ServletResponse response) throws Exception {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(403);
httpResponse.getWriter().write("{\"error\":\"IP not allowed\"}");
return false;
}

private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}

七、Shiro 会话管理

7.1 Shiro 会话概述

Shiro 提供了一套独立于容器的会话管理框架,具有以下特点:

7.2 Session API 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Shiro Session 使用示例
*/
public class SessionDemo {

public void sessionDemo() {
Subject currentUser = SecurityUtils.getSubject();

// 获取会话(如果不存在则创建)
Session session = currentUser.getSession();

// 设置会话属性
session.setAttribute("userId", 1001L);
session.setAttribute("username", "admin");

// 获取会话属性
Long userId = (Long) session.getAttribute("userId");

// 获取会话信息
session.getId(); // 会话 ID
session.getStartTimestamp(); // 创建时间
session.getLastAccessTime(); // 最后访问时间
session.getTimeout(); // 超时时间

// 设置超时时间(毫秒)
session.setTimeout(1800000); // 30 分钟

// 使会话失效
session.stop();
}
}

7.3 会话监听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 自定义会话监听器
*/
public class CustomSessionListener implements SessionListener {

private final Logger logger = LoggerFactory.getLogger(getClass());

@Override
public void onStart(Session session) {
logger.info("会话创建:{}", session.getId());
}

@Override
public void onStop(Session session) {
logger.info("会话停止:{}", session.getId());
}

@Override
public void onExpiration(Session session) {
logger.info("会话过期:{}", session.getId());
}
}

/**
* 配置会话监听器
*/
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();

// 设置会话超时
sessionManager.setGlobalSessionTimeout(1800000); // 30 分钟

// 配置监听器
List<SessionListener> listeners = new ArrayList<>();
listeners.add(new CustomSessionListener());
sessionManager.setSessionListeners(listeners);

return sessionManager;
}

八、Shiro 加密与密码管理

8.1 Shiro 加密支持

Shiro 提供了完整的加密支持,包括对称加密、非对称加密和哈希加密:

8.2 Hash 加密详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Hash 加密工具类
*/
public class HashUtil {

/**
* MD5 加密(简单)
*/
public static String md5(String input) {
return new SimpleHash("MD5", input).toHex();
}

/**
* SHA-256 加密(推荐)
*/
public static String sha256(String input) {
return new SimpleHash("SHA-256", input).toHex();
}

/**
* 带盐 SHA-256 加密
*/
public static String sha256WithSalt(String input, String salt) {
SimpleHash hash = new SimpleHash(
"SHA-256",
input,
salt,
1024 // 迭代次数
);
return hash.toHex();
}

/**
* 生成随机盐值
*/
public static String generateSalt() {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}

/**
* 验证密码
*/
public static boolean verifyPassword(String rawPassword,
String storedPasswordHash,
String salt) {
String calculatedHash = sha256WithSalt(rawPassword, salt);
return calculatedHash.equals(storedPasswordHash);
}
}

8.3 Shiro 内置加密工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* Shiro Codec 模块提供的加密工具
*/
public class ShiroCodecDemo {

public static void main(String[] args) {
// 1. Base64 编码解码
String original = "Hello, Shiro! 🛡️";
String encoded = Base64.encodeToString(original.getBytes());
byte[] decoded = Base64.decode(encoded);

// 2. Hex 编码解码
String hex = Hex.encodeToString(original.getBytes());
byte[] fromHex = Hex.decode(hex);

// 3. AesCipherService(AES 加密)
AesCipherService aes = new AesCipherService();
aes.setKeySize(128); // 128/256 位

// 加密
byte[] encrypted = aes.encrypt(
original.getBytes(),
key.getBytes()
).getBytes();

// 解密
byte[] decrypted = aes.decrypt(encrypted, key.getBytes()).getBytes();

// 4. DefaultPasswordService(密码服务)
DefaultPasswordService passwordService = new DefaultPasswordService();
String hashedPassword = passwordService.encryptPassword("password123");
boolean matches = passwordService.passwordsMatch("password123", hashedPassword);
}
}

九、Shiro 与 Spring Boot 集成

9.1 Maven 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- Shiro Spring Boot 集成 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.13.0</version>
</dependency>

<!-- Shiro Session Redis 存储(可选) -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-redis</artifactId>
<version>1.13.0</version>
</dependency>

9.2 Shiro 配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* Shiro 配置类
*/
@Configuration
public class ShiroConfig {

/**
* 配置 SecurityManager
*/
@Bean
public SecurityManager securityManager(
CustomRealm customRealm,
SessionManager sessionManager,
CacheManager cacheManager) {

DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(customRealm);
manager.setSessionManager(sessionManager);
manager.setCacheManager(cacheManager);

return manager;
}

/**
* 配置 ShiroFilterChainDefinition
* 定义 URL 与过滤器的映射关系
*/
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition =
new DefaultShiroFilterChainDefinition();

// 公开路径
chainDefinition.addPathDefinition("/login", "anon");
chainDefinition.addPathDefinition("/register", "anon");
chainDefinition.addPathDefinition("/static/**", "anon");

// 需要认证的路径
chainDefinition.addPathDefinition("/user/**", "authc");
chainDefinition.addPathDefinition("/admin/**", "authc,roles[admin]");

// 登出
chainDefinition.addPathDefinition("/logout", "logout");

// 其他全部需要认证
chainDefinition.addPathDefinition("/**", "authc");

return chainDefinition;
}

/**
* 启用 Shiro 注解
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor =
new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}

/**
* 启用 Spring AOP 支持
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
}

9.3 完整配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* 完整的 Shiro + Spring Boot 配置
*/
@Configuration
@EnableAspectJAutoProxy
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfig {

@Autowired
private ShiroProperties shiroProperties;

@Bean
public CustomRealm customRealm() {
CustomRealm realm = new CustomRealm();

// 配置密码加密器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("SHA-256");
matcher.setHashIterations(shiroProperties.getHashIterations());
matcher.setStoredCredentialsHexEncoded(false);
realm.setCredentialsMatcher(matcher);

// 配置缓存
realm.setCacheManager(shiroRedisCacheManager());

return realm;
}

@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(customRealm());
manager.setSessionManager(sessionManager());
manager.setCacheManager(shiroRedisCacheManager());
return manager;
}

@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager manager = new DefaultWebSessionManager();
manager.setGlobalSessionTimeout(shiroProperties.getSessionTimeout());
manager.setSessionIdCookieEnabled(true);
manager.setSessionIdUrlRewritingEnabled(false);
return manager;
}

@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition definition =
new DefaultShiroFilterChainDefinition();

// 公开路径
definition.addPathDefinition("/api/auth/**", "anon");

// 需要认证
definition.addPathDefinition("/api/user/**", "authc");

// 需要管理员角色
definition.addPathDefinition("/api/admin/**", "authc,roles[admin]");

return definition;
}
}

十、Shiro 与 Redis 集成

10.1 为什么要使用 Redis 存储会话?

10.2 Shiro Redis 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* Shiro Redis 配置
*/
@Configuration
public class ShiroRedisConfig {

@Autowired
private RedisConnectionFactory redisConnectionFactory;

/**
* 配置 Redis 管理器
*/
@Bean
public RedisManager shiroRedisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setConnectionFactory(redisConnectionFactory);
redisManager.setExpire(1800); // 30 分钟过期
redisManager.setTimeout(5000); // 5 秒超时
redisManager.setKeyPrefix("shiro:session:");
return redisManager;
}

/**
* 配置 Session DAO
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(shiroRedisManager());
return sessionDAO;
}

/**
* 配置缓存 DAO
*/
@Bean
public RedisCacheManager shiroRedisCacheManager() {
RedisCacheManager cacheManager = new RedisCacheManager();
cacheManager.setRedisManager(shiroRedisManager());
cacheManager.setKeyPrefix("shiro:cache:");
cacheManager.setExpire(3600); // 1 小时过期
return cacheManager;
}

/**
* 配置 Session Manager 使用 Redis
*/
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager manager = new DefaultWebSessionManager();
manager.setSessionDAO(redisSessionDAO());
manager.setGlobalSessionTimeout(1800000); // 30 分钟
return manager;
}
}

十一、常见问题与最佳实践

11.1 常见问题与解决方案

11.2 Shiro 配置清单

配置项 推荐设置 说明
密码加密 SHA-256 + 盐 + 多次迭代 安全可靠
会话超时 1800 秒(30 分钟) 平衡安全与体验
记住我 可选,最长 7 天 需评估风险
CSRF 启用 防止跨站请求
Session Redis 推荐生产环境使用 支持分布式

11.3 安全建议

安全建议 说明
使用 HTTPS 生产环境必须启用
密码加密 避免 MD5 单一加密
会话管理 生产环境使用 Redis 存储
权限最小化 只授予必要的权限
日志审计 记录登录和敏感操作

十二、总结

12.1 核心知识点回顾

12.2 学习路线建议

12.3 Shiro 与 Spring Security 选择

场景 推荐
Spring Boot 全家桶项目 Spring Security
轻量级独立项目 Shiro
快速开发上线 Shiro
复杂的企业级安全需求 Spring Security
微服务架构 Spring Security
非 Spring 项目 Shiro

💡 写给读者的话:Shiro 是一个简洁而不简单的安全框架,它的设计理念是”让安全变得简单”。相比 Spring Security,Shiro 的配置更加直观,非常适合快速上手。希望本文能帮助你快速掌握 Shiro,在项目中实现安全保护!🛡️


📅 本文首次发布于 2026 年 5 月 24 日