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 对比
flowchart LR
A["🛡️ Java 安全框架对比"] --> B["🔐 Shiro"]
A --> C["🔒 Spring Security"]
B --> B1["✅ 配置简洁\n学习曲线平缓"]
B --> B2["✅ 轻量级\n依赖少"]
B --> B3["✅ 独立使用\n不依赖 Spring"]
B --> B4["⚠️ 功能相对较少\n社区较小"]
C --> C1["✅ 功能完善\nSpring 生态深度集成"]
C --> C2["✅ 社区活跃\n持续更新"]
C --> C3["⚠️ 配置复杂\n学习曲线陡峭"]
C --> C4["⚠️ 强依赖 Spring"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#e3f2fd
对比维度
Shiro
Spring Security
学习曲线
平缓,易上手
陡峭
配置复杂度
简单,INI 配置
复杂,Java 配置
依赖
独立,依赖少
强依赖 Spring
社区活跃度
一般
非常活跃
文档
英文为主
中文资料丰富
适用场景
轻量级应用、微服务
企业级应用、Spring 全家桶
与 Spring Boot 集成
shiro-spring-boot-web-starter
spring-boot-starter-security
1.3 Shiro 核心功能
flowchart TD
A["🛡️ Shiro 核心功能"] --> B["🕵️ Authentication\n身份认证"]
A --> C["🔑 Authorization\n授权"]
A --> D["🔐 Cryptography\n加密"]
A --> E["📦 Session Management\n会话管理"]
A --> F["🌐 Web Support\nWeb 支持"]
A --> G["🧩 Caching\n缓存支持"]
B --> B1["用户名密码\n手机验证码\n第三方登录"]
C --> C1["角色权限\n资源权限\nRBAC 模型"]
D --> D1["MD5/SHA\nAES/RSA\n密码哈希"]
E --> E1["Session API\n分布式会话\n会话监听"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
style E fill:#fff3e0
style F fill:#f8bbd0
style G fill:#e3f2fd
1.4 Shiro 特性一览
特性
说明
易用性
API 简洁直观,上手快
独立性
不依赖容器,可独立运行
灵活性
可在任意 Java 程序中使用
Web 支持
强大的 URL 过滤和拦截
可扩展
自定义组件易于替换
Session
独立于容器的会话管理
缓存
支持 Ehcache、Redis 等缓存
测试
支持 JUnit、TestNG 等测试框架
二、Shiro 核心架构与概念 2.1 Shiro 架构图 Shiro 的核心架构由三个主要部分组成:
flowchart TD
A["🧱 Shiro 核心架构"] --> B["📱 Subject\n主体"]
A --> C["🔧 SecurityManager\n安全管理器"]
A --> D["🗄️ Realm\n领域"]
B --> B1["当前操作用户\n可以是人\n也可以是服务"]
C --> C1["Shiro 核心\n管理所有组件"]
C1 --> C2["Authenticator\n认证器"]
C1 --> C3["Authorizer\n授权器"]
C1 --> C4["SessionManager\n会话管理器"]
C1 --> C5["CacheManager\n缓存管理器"]
D --> D1["连接 SecurityManager\n和程序数据\n(用户、角色、权限)"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#e3f2fd
style D fill:#fff3e0
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 的核心管理器,协调所有安全相关的组件 。
flowchart TD
A["🔧 SecurityManager"] --> B["Authenticator\n认证器"]
A --> C["Authorizer\n授权器"]
A --> D["SessionManager\n会话管理器"]
A --> E["CacheManager\n缓存管理器"]
B --> B1["认证策略\n(AtLeastOneSuccessfulStrategy)"]
C --> C1["权限模式\n(AuthorizingRealm)"]
D --> D1["NativeSessionManager\n(原生会话)"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
style E fill:#fff3e0
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 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(), user.getPassword(), getName() ); } @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 认证与授权流程
flowchart TD
A["🔐 Shiro 认证授权流程"] --> B["1️⃣ Subject\n提交凭证"]
B --> C["2️⃣ SecurityManager\n接收请求"]
C --> D["3️⃣ Authenticator\n执行认证"]
D --> E["4️⃣ Realm\n查询用户数据"]
E --> F{"认证成功?"}
F -->|"是"| G["5️⃣ 创建\nAuthenticationInfo"]
F -->|"否"| H["6️⃣ 抛出\nAuthenticationException"]
G --> I["7️⃣ Subject\n绑定会话"]
I --> J["8️⃣ 授权检查\n(按需触发)"]
J --> K{"有权限?"}
K -->|"是"| L["✅ 允许访问"]
K -->|"否"| M["❌ 拒绝访问"]
style A fill:#fff3e0
style G fill:#c8e6c9
style H fill:#ffcdd2
style L fill:#c8e6c9
style M fill:#ffcdd2
三、快速入门:五分钟跑通 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 <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-core</artifactId > <version > 1.13.0</version > </dependency > <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-web</artifactId > <version > 1.13.0</version > </dependency > <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-spring-boot-web-starter</artifactId > <version > 1.13.0</version > </dependency > <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 [main] credentialsMatcher =org.apache.shiro.authc.credential.HashedCredentialsMatchercredentialsMatcher.hashAlgorithmName =MD5credentialsMatcher.hashIterations =2 credentialsMatcher.storedCredentialsHexEncoded =true customRealm =com.example.realm.CustomRealmcustomRealm.credentialsMatcher =$credentialsMatcher cacheManager =org.apache.shiro.cache.MemoryConstrainedCacheManagercustomRealm.cacheManager =$cacheManager securityManager.realms =$customRealm securityManager.cacheManager =$cacheManager [users] admin =admin123,admin,usertest =test123,user[roles] admin =*user =article:read,article:write[urls] /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 public class ShiroHelloWorld { public static void main (String[] args) { Factory<SecurityManager> factory = new IniSecurityManagerFactory ("classpath:shiro.ini" ); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); Subject currentUser = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken ("admin" , "admin123" ); try { currentUser.login(token); System.out.println("✅ 登录成功!" ); if (currentUser.hasRole("admin" )) { System.out.println("👤 用户是管理员" ); } 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 { currentUser.logout(); } } }
四、Shiro 认证流程详解 4.1 AuthenticationToken 详解 AuthenticationToken 是用户身份凭证的抽象,Shiro 使用它来携带认证信息:
1 2 3 4 5 6 7 8 9 10 11 UsernamePasswordToken token = new UsernamePasswordToken ( "username" , "password" ); token.setRememberMe(true ); token.setHost("192.168.1.1" );
4.2 AuthenticationInfo 详解 AuthenticationInfo 是认证信息的抽象,包含三个核心元素:
flowchart TD
A["📋 AuthenticationInfo"] --> B["Principal\n身份标识"]
A --> C["Credential\n凭证"]
A --> D["Realm Name\nRealm 名称"]
B --> B1["用户名\n用户 ID\n用户对象"]
C --> C1["密码\n证书"]
D --> D1["来自哪个\nRealm 的认证"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
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 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 ("用户不存在" ); } return new SimpleAuthenticationInfo ( user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName() ); } }
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 matcher = new HashedCredentialsMatcher (); matcher.setHashAlgorithmName("SHA-256" ); matcher.setHashIterations(1024 ); matcher.setStoredCredentialsHexEncoded(false ); 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 UsernamePasswordToken token = new UsernamePasswordToken (username, password);UsernamePasswordToken token = new UsernamePasswordToken (username, password, rememberMe);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; } } 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 授权的三种方式
flowchart TD
A["🔑 授权方式"] --> B["1️⃣ 编程式授权"]
A --> C["2️⃣ 注解式授权"]
A --> D["3️⃣ 标签式授权"]
B --> B1["在代码中\n手动判断"]
C --> C1["使用注解\n@RequiresRoles\n@RequiresPermissions"]
D --> D1["在页面模板中\n使用 Shiro 标签"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
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" ))) { } 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 { @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 "user:create" "user:create,read,update" "user:*" "*:*" "system:user:manage"
六、Shiro 内置过滤器 6.1 过滤器配置 Shiro 通过过滤器链来拦截请求并进行安全控制:
1 2 3 4 5 6 7 8 [urls] /login = anon /logout = logout /static/** = anon /admin/** = authc, roles[admin] /user/** = authc /** = anon
6.2 常用过滤器一览
flowchart TD
A["🔗 Shiro 内置过滤器"] --> B["anon\n匿名访问"]
A --> C["authc\n需要认证"]
A --> D["authcBasic\nHTTP Basic"]
A --> E["roles\n角色检查"]
A --> F["perms\n权限检查"]
A --> G["ssl\nHTTPS"]
B --> B1["无需认证\n可自由访问"]
C --> C1["必须成功认证\n否则跳转登录"]
D --> D1["需要 HTTP\nBasic 认证"]
E --> E1["必须拥有\n指定角色"]
F --> F1["必须拥有\n指定权限"]
G --> G1["必须使用\nHTTPS 协议"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
style E fill:#fff3e0
style F fill:#f8bbd0
style G fill:#e3f2fd
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] /login = anon /register = anon /captcha = anon /home = authc /profile = authc /admin/** = authc, roles[admin] /manager/** = authc, roles[admin, manager] /user/create = authc, perms[user:create] /user/delete = authc, perms[user:delete] /api/** = authcBasic /payment/** = ssl[443] /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 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 提供了一套独立于容器 的会话管理框架,具有以下特点:
flowchart TD
A["📦 Shiro 会话特性"] --> B["🧬 容器无关性"]
A --> C["🕐 可配置超时"]
A --> D["🌐 Web 支持"]
A --> E["📊 会话监听"]
A --> F["💾 会话存储"]
B --> B1["不依赖 Servlet 容器\n可在普通 Java 程序中使用"]
C --> C1["可设置会话\n超时时间"]
D --> D1["提供 Web SessionFilter\n自动管理 Web 会话"]
E --> E1["监听会话创建\n销毁等事件"]
F --> F1["支持内存、Redis\nEhcache 等存储"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
style E fill:#fff3e0
style F fill:#f8bbd0
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 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(); session.getStartTimestamp(); session.getLastAccessTime(); session.getTimeout(); session.setTimeout(1800000 ); 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 ); List<SessionListener> listeners = new ArrayList <>(); listeners.add(new CustomSessionListener ()); sessionManager.setSessionListeners(listeners); return sessionManager; }
八、Shiro 加密与密码管理 8.1 Shiro 加密支持 Shiro 提供了完整的加密支持,包括对称加密、非对称加密和哈希加密:
flowchart TD
A["🔐 Shiro 加密支持"] --> B["🧊 Hash 加密"]
A --> C["🔑 AES/RSA"]
A --> D["📝 Base64"]
B --> B1["MD5/SHA\n带盐迭代"]
C --> C1["对称/非对称\n加密解密"]
D --> D1["编码解码\n数据传输"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
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 public class HashUtil { public static String md5 (String input) { return new SimpleHash ("MD5" , input).toHex(); } public static String sha256 (String input) { return new SimpleHash ("SHA-256" , input).toHex(); } 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 public class ShiroCodecDemo { public static void main (String[] args) { String original = "Hello, Shiro! 🛡️" ; String encoded = Base64.encodeToString(original.getBytes()); byte [] decoded = Base64.decode(encoded); String hex = Hex.encodeToString(original.getBytes()); byte [] fromHex = Hex.decode(hex); AesCipherService aes = new AesCipherService (); aes.setKeySize(128 ); byte [] encrypted = aes.encrypt( original.getBytes(), key.getBytes() ).getBytes(); byte [] decrypted = aes.decrypt(encrypted, key.getBytes()).getBytes(); 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 <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-spring-boot-web-starter</artifactId > <version > 1.13.0</version > </dependency > <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 @Configuration public class ShiroConfig { @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; } @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; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor ( SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor (); advisor.setSecurityManager(securityManager); return advisor; } @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 @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 存储会话?
flowchart LR
A["❌ 单机会话问题"] --> A1["无法跨服务器\n共享会话"]
A1 --> A2["服务器重启\n会话丢失"]
B["✅ Redis 存储优势"] --> B1["跨服务器\n共享会话"]
B1 --> B2["服务器重启\n会话不丢失"]
B1 --> B3["支持分布式\n集群部署"]
style A fill:#ffcdd2
style B fill:#c8e6c9
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 @Configuration public class ShiroRedisConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public RedisManager shiroRedisManager () { RedisManager redisManager = new RedisManager (); redisManager.setConnectionFactory(redisConnectionFactory); redisManager.setExpire(1800 ); redisManager.setTimeout(5000 ); redisManager.setKeyPrefix("shiro:session:" ); return redisManager; } @Bean public RedisSessionDAO redisSessionDAO () { RedisSessionDAO sessionDAO = new RedisSessionDAO (); sessionDAO.setRedisManager(shiroRedisManager()); return sessionDAO; } @Bean public RedisCacheManager shiroRedisCacheManager () { RedisCacheManager cacheManager = new RedisCacheManager (); cacheManager.setRedisManager(shiroRedisManager()); cacheManager.setKeyPrefix("shiro:cache:" ); cacheManager.setExpire(3600 ); return cacheManager; } @Bean public SessionManager sessionManager () { DefaultWebSessionManager manager = new DefaultWebSessionManager (); manager.setSessionDAO(redisSessionDAO()); manager.setGlobalSessionTimeout(1800000 ); return manager; } }
十一、常见问题与最佳实践 11.1 常见问题与解决方案
flowchart TD
A["❓ 常见问题"] --> B["⚠️ Subject 不为 null\n但未认证"]
A --> C["⚠️ 过滤器链不生效"]
A --> D["⚠️ 会话 ID 变化"]
A --> E["⚠️ Redis 会话\n无法共享"]
B --> B1["SecurityManager\n未正确配置"]
B1 --> B2["检查 Realm 配置\n是否注入到 SecurityManager"]
C --> C1["URL 匹配规则\n顺序问题"]
C1 --> C2["确保路径配置\n从上到下匹配"]
D --> D1["会话固定攻击防护"]
D1 --> D2["启用会话固定防护"]
E --> E1["Redis Key 冲突"]
E1 --> E2["检查 Key 前缀\n是否一致"]
style A fill:#fff3e0
11.2 Shiro 配置清单
配置项
推荐设置
说明
密码加密
SHA-256 + 盐 + 多次迭代
安全可靠
会话超时
1800 秒(30 分钟)
平衡安全与体验
记住我
可选,最长 7 天
需评估风险
CSRF
启用
防止跨站请求
Session Redis
推荐生产环境使用
支持分布式
11.3 安全建议
安全建议
说明
使用 HTTPS
生产环境必须启用
密码加密
避免 MD5 单一加密
会话管理
生产环境使用 Redis 存储
权限最小化
只授予必要的权限
日志审计
记录登录和敏感操作
十二、总结 12.1 核心知识点回顾
mindmap
root((Apache Shiro))
核心概念
Subject 主体
SecurityManager 安全管理器
Realm 领域
认证
AuthenticationToken
AuthenticationInfo
密码加密
授权
编程式授权
注解式授权
标签式授权
过滤器
内置过滤器
自定义过滤器
过滤器链配置
会话管理
Session API
SessionListener
Redis 存储
Spring Boot
集成配置
Redis 会话
注解启用
12.2 学习路线建议
flowchart LR
A["Shiro 学习路线"] --> B["第一阶段\n基础概念"]
B --> C["第二阶段\n认证授权"]
C --> D["第三阶段\n过滤器"]
D --> E["第四阶段\n会话管理"]
E --> F["第五阶段\nSpring Boot"]
B --> B1["架构组件\n核心接口"]
C --> C1["Realm 实现\n权限控制"]
D --> D1["内置过滤器\n自定义过滤器"]
E --> E1["Session API\nRedis 集成"]
F --> F1["配置集成\n项目实战"]
style A fill:#fff3e0
style B fill:#e3f2fd
style C fill:#c8e6c9
style D fill:#fff3e0
style E fill:#f8bbd0
style F fill:#e3f2fd
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 日