当前位置: 首页 > news >正文

Apache Shiro

一、Apache Shiro 使用与配置教程

Apache Shiro 是一个强大的 Java 安全框架,用于管理用户认证、授权、加密和会话管理。它以其简单性、灵活性和易用性著称,提供了丰富的 API 和配置选项,帮助开发者快速实现应用程序的安全控制。下面我将一步步指导您完成 Shiro 的基本配置和使用方法,基于真实可靠的实践参考。

1. 准备工作

在开始配置前,确保您的开发环境已就绪:

  • 安装 Java JDK(建议 JDK 8 或更高版本)。
  • 使用 Maven 或 Gradle 作为构建工具(例如,在 pom.xml 中添加 Shiro 依赖)。
  • 准备一个简单的 Java Web 项目(如基于 Spring Boot)。
    Shiro 的优势在于其轻量级设计,能轻松集成到各种框架中。
2. 核心概念理解

Shiro 的核心功能包括:

  • 身份验证(Authentication):验证用户身份,例如登录检查。
  • 授权(Authorization):控制用户访问权限,如角色或资源权限。
  • 加密(Cryptography):保护敏感数据(如密码)。
  • 会话管理(Session Management):管理用户会话状态。
    这些功能通过 Shiro 的 API 实现,简化了安全逻辑的开发。
3. 配置教程(以 Spring Boot 为例)

下面是一个完整的 Shiro 配置步骤。假设您已创建一个 Spring Boot 项目。

步骤 1: 添加依赖

pom.xml 文件中添加 Shiro 和 Spring Boot Starter 依赖:

<dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.10.0</version> <!-- 使用最新稳定版 --></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>
步骤 2: 创建 Shiro 配置类

在项目中创建一个 Java 类(如 ShiroConfig.java),定义 Shiro 的安全管理器和 Realm(用于数据源连接)。Realm 是 Shiro 的核心组件,负责从数据库或其他源获取用户和权限信息。

import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ShiroConfig {// 定义 Realm(这里使用简单的 IniRealm 作为示例)@Beanpublic IniRealm realm() {IniRealm realm = new IniRealm();realm.setResourcePath("classpath:shiro-users.ini"); // 从文件加载用户数据return realm;}// 配置安全管理器@Beanpublic DefaultWebSecurityManager securityManager(IniRealm realm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(realm);return securityManager;}// 定义 URL 过滤规则@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition() {DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();chainDefinition.addPathDefinition("/login", "anon"); // 允许匿名访问登录页chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]"); // 需要认证和 admin 角色chainDefinition.addPathDefinition("/**", "authc"); // 其他路径需认证return chainDefinition;}
}
步骤 3: 创建用户数据文件

src/main/resources 目录下创建 shiro-users.ini 文件,定义测试用户和角色:

[users]
# 格式: 用户名 = 密码, 角色1, 角色2
admin = password123, admin
user = password456, user[roles]
# 定义角色权限
admin = *  # 所有权限
user = user:read  # 只读权限
步骤 4: 实现登录控制器

创建一个简单的 Spring Boot 控制器(如 LoginController.java)来处理登录逻辑:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class LoginController {@PostMapping("/login")public String login(String username, String password) {Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username, password);try {subject.login(token); // 执行 Shiro 认证return "登录成功!";} catch (Exception e) {return "登录失败:" + e.getMessage();}}
}
步骤 5: 启动并测试

运行 Spring Boot 应用(使用 mvn spring-boot:run 或 IDE 启动)。访问 /login 端点,传递用户名和密码进行测试。例如:

  • admin/password123 登录,可访问 /admin 路径。
  • user/password456 登录,仅能访问用户相关资源。
4. 高级配置与优化
  • 集成数据库:替换 IniRealmJdbcRealm,连接 MySQL 或 PostgreSQL 存储用户数据。
  • 加密配置:使用 Shiro 的 HashedCredentialsMatcher 对密码进行 SHA-256 加密。
  • 会话管理:通过 SessionManager 配置会话超时或集群共享。
    Shiro 的灵活性允许您根据需求扩展,例如添加自定义 Realm 或过滤器。
5. 总结

Apache Shiro 简化了 Java 应用的安全开发,通过上述配置,您可以在几分钟内实现基本的认证和授权功能。Shiro 的成熟性和可靠性使其成为企业级应用的理想选择,能显著提升应用程序的安全性。

思维导图

在这里插入图片描述


二、Apache Shiro 核心原理详解

1. 核心架构与运行原理

Apache Shiro 采用分层架构设计,核心组件包括:

  • Subject:当前用户的安全上下文,封装认证和授权操作
  • SecurityManager:核心协调器,管理所有 Subject(单例模式)
  • Realm:安全数据源(数据库/LDAP/文件等)的桥梁组件
  • SessionManager:会话生命周期管理
  • CacheManager:权限数据缓存优化
请求
认证
授权
会话
数据源
数据源
缓存
Subject
SecurityManager
Authenticator
Authorizer
SessionManager
Realm
CacheManager

认证流程源码解析DefaultSecurityManager.login()):

public Subject login(Subject subject, AuthenticationToken token) {// 1. 创建认证上下文AuthenticationInfo info = authenticate(token);// 2. 创建认证会话Subject loggedIn = createSubject(token, info, subject);// 3. 发布认证事件notifyLoginListeners(loggedIn);return loggedIn;
}
2. 核心组件功能详解
组件功能特点
Subject用户安全操作入口线程安全,自动绑定当前线程
Realm安全数据访问层可扩展,支持多数据源集成
Authenticator认证逻辑执行器支持多Realm认证策略
Authorizer授权决策引擎RBAC/ABAC混合支持
SessionDAO会话持久化支持Redis/Memcached集群

自定义Realm示例

public class JdbcRealm extends AuthorizingRealm {// 认证实现protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {String username = (String) token.getPrincipal();User user = userService.findByUsername(username);return new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()),getName());}// 授权实现protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {User user = (User) principals.getPrimaryPrincipal();SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setRoles(roleService.getRoles(user.getId()));info.setStringPermissions(permService.getPerms(user.getId()));return info;}
}
3. 常用注解原理与使用

Shiro 通过 Spring AOP 实现注解权限控制:

  1. AuthorizationAttributeSourceAdvisor 扫描注解
  2. AopAllianceAnnotationsAuthorizingMethodInterceptor 拦截方法
  3. 调用 AuthorizingAnnotationMethodInterceptor 执行权限校验

常用注解

注解功能示例
@RequiresAuthentication需登录@RequiresAuthentication
@RequiresPermissions需权限@RequiresPermissions("user:delete")
@RequiresRoles需角色@RequiresRoles("admin")
@RequiresUser需已认证或记住我@RequiresUser

注解实现源码

// 权限注解拦截器
public class PermissionAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {public void assertAuthorized(MethodInvocation mi) {RequiresPermissions requiresPerm = mi.getMethod().getAnnotation(RequiresPermissions.class);Subject subject = getSubject();// 检查权限逻辑String[] perms = requiresPerm.value();subject.checkPermissions(perms);}
}
4. 关键源码解析

认证执行流程AuthenticatingRealm.getAuthenticationInfo()):

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) {// 1. 从缓存获取认证信息AuthenticationInfo info = getCachedAuthenticationInfo(token);if (info == null) {// 2. 调用Realm实现获取数据info = doGetAuthenticationInfo(token);// 3. 凭证匹配验证assertCredentialsMatch(token, info);// 4. 缓存结果cacheAuthenticationInfoIfPossible(token, info);}return info;
}

授权决策树ModularRealmAuthorizer.isPermitted()):

public boolean isPermitted(PrincipalCollection principals, String permission) {// 1. 获取所有RealmCollection<Realm> realms = getRealms();// 2. 遍历Realm进行权限验证for (Realm realm : realms) {if (!(realm instanceof Authorizer)) continue;if (((Authorizer) realm).isPermitted(principals, permission)) {return true; // 任一Realm通过即授权}}return false;
}
5. 高级特性实现

会话集群共享(Redis 集成):

@Bean
public SessionManager sessionManager() {DefaultWebSessionManager manager = new DefaultWebSessionManager();manager.setSessionDAO(redisSessionDAO());manager.setSessionIdCookie(sessionIdCookie());return manager;
}@Bean
public SessionDAO redisSessionDAO() {RedisSessionDAO dao = new RedisSessionDAO();dao.setRedisManager(redisManager());dao.setExpire(1800); // 30分钟超时return dao;
}

密码加密配置

@Bean
public HashedCredentialsMatcher credentialsMatcher() {HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();matcher.setHashAlgorithmName("SHA-256");matcher.setHashIterations(1024);matcher.setStoredCredentialsHexEncoded(false);return matcher;
}
总结

Apache Shiro 的核心优势在于其模块化架构和可扩展性:

  1. 认证流程:通过 Realm 解耦数据源,支持多因素认证
  2. 授权机制:基于注解的声明式权限控制,支持细粒度授权
  3. 会话管理:可插拔的 SessionDAO 实现分布式会话
  4. 密码安全:内置多种加密算法和盐值支持

Shiro 通过简洁的 API 和强大的扩展性,为 Java 应用提供了企业级安全解决方案。


三、Apache Shiro 深度解析与实践

一、基于数据库的权限管理实现

Shiro 通过 Realm 组件连接数据库实现权限管理,核心流程如下:

SubjectSecurityManagerRealmDatabase提交认证请求调用doGetAuthenticationInfo()查询用户信息返回用户凭证封装AuthenticationInfo返回认证结果请求权限验证调用doGetAuthorizationInfo()查询角色/权限返回权限数据封装AuthorizationInfo返回授权结果SubjectSecurityManagerRealmDatabase

数据库表结构示例

-- 用户表
CREATE TABLE users (id BIGINT PRIMARY KEY,username VARCHAR(50) UNIQUE,password VARCHAR(100),salt VARCHAR(50)
);-- 角色表
CREATE TABLE roles (id BIGINT PRIMARY KEY,role_name VARCHAR(50) UNIQUE
);-- 权限表
CREATE TABLE permissions (id BIGINT PRIMARY KEY,perm_name VARCHAR(100) UNIQUE
);-- 用户-角色关联表
CREATE TABLE user_roles (user_id BIGINT,role_id BIGINT,PRIMARY KEY(user_id, role_id)
);-- 角色-权限关联表
CREATE TABLE role_perms (role_id BIGINT,perm_id BIGINT,PRIMARY KEY(role_id, perm_id)
);

自定义数据库 Realm 实现

public class DatabaseRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {String username = (String) token.getPrincipal();User user = userService.findByUsername(username);if (user == null) return null;return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String username = (String) principals.getPrimaryPrincipal();SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 添加角色Set<String> roles = userService.getUserRoles(username);info.setRoles(roles);// 添加权限Set<String> permissions = userService.getUserPermissions(username);info.setStringPermissions(permissions);return info;}
}
二、分布式会话共享机制

Shiro 通过 SessionDAO 实现分布式会话管理:

Redis 会话共享配置

@Bean
public RedisSessionDAO redisSessionDAO() {RedisSessionDAO dao = new RedisSessionDAO();dao.setRedisManager(redisManager());dao.setExpire(1800); // 30分钟超时dao.setKeyPrefix("shiro_session:");return dao;
}@Bean
public SessionManager sessionManager() {DefaultWebSessionManager manager = new DefaultWebSessionManager();manager.setSessionDAO(redisSessionDAO());manager.setSessionIdCookieEnabled(true);manager.setSessionIdUrlRewritingEnabled(false);return manager;
}@Bean
public RedisManager redisManager() {RedisManager manager = new RedisManager();manager.setHost("redis-cluster.example.com:6379");manager.setTimeout(2000);manager.setPassword("secure_pass");return manager;
}

会话同步原理

  1. 用户请求到达时,Shiro 从 Cookie 获取 SessionID
  2. 通过 SessionDAO 从 Redis 加载 Session 数据
  3. 请求处理期间修改的 Session 属性自动标记为脏数据
  4. 请求结束时,脏数据通过 SessionDAO 写回 Redis
三、Subject 线程绑定机制

实现原理

// ThreadContext 维护线程本地变量
public class ThreadContext {private static final ThreadLocal<Map<Object, Object>> resources =new InheritableThreadLocalMap<>();public static void bind(Subject subject) {resources.get().put(SUBJECT_KEY, subject);}public static Subject getSubject() {return (Subject) resources.get().get(SUBJECT_KEY);}
}// SecurityManager 绑定 Subject
public class DefaultSecurityManager {public Subject createSubject(SubjectContext context) {// 创建 Subject 实例Subject subject = doCreateSubject(context);// 绑定到当前线程ThreadContext.bind(subject);return subject;}
}

线程绑定特点

  1. 使用 InheritableThreadLocal 支持线程池环境
  2. 请求结束时自动解除绑定
  3. 支持手动绑定/解绑特殊场景
  4. 每个线程独立存储 Subject 实例
四、ABAC 权限策略扩展

实现 ABAC 的步骤

  1. 创建属性收集器
public class UserAttributeSource {public Map<String, Object> getAttributes(Subject subject) {Map<String, Object> attrs = new HashMap<>();attrs.put("department", subject.getDepartment());attrs.put("securityLevel", subject.getSecurityLevel());attrs.put("loginTime", LocalDateTime.now());return attrs;}
}
  1. 实现 ABAC Realm
public class ABACRealm extends AuthorizingRealm {@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return new SimpleAuthorizationInfo(); // 返回空权限集}@Overridepublic boolean isPermitted(PrincipalCollection principals, String permission) {// 获取属性Subject subject = SecurityUtils.getSubject();Map<String, Object> attributes = attributeSource.getAttributes(subject);// 解析权限表达式Permission abacPerm = new AbacPermission(permission);// ABAC 决策return abacPerm.implies(attributes);}
}
  1. 定义 ABAC 权限表达式
// 示例: 仅允许研发部且安全等级>3的用户在9:00-18:00访问
@RequiresPermission("action:access & department:RD & securityLevel>3 & time:9:00-18:00")
public void accessResource() {// 受保护资源
}
五、OAuth2 集成实践

Shiro + OAuth2 集成架构

1. 授权请求
2. 授权码
3. 令牌请求
4. 访问令牌
5. 携带令牌
6. 令牌验证
7. 用户信息
8. 资源响应
客户端
OAuth2 服务端
资源服务器
Shiro Realm

OAuth2Realm 实现

public class OAuth2Realm extends AuthorizingRealm {@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof OAuth2Token;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {OAuth2Token oauthToken = (OAuth2Token) token;// 验证访问令牌AccessToken accessToken = oauthServer.verifyToken(oauthToken.getCredentials());if (accessToken == null || accessToken.isExpired()) {throw new ExpiredCredentialsException();}// 获取用户信息UserInfo userInfo = oauthServer.getUserInfo(accessToken);return new SimpleAuthenticationInfo(userInfo.getUsername(),accessToken.getValue(),getName());}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 从OAuth服务获取权限String username = (String) principals.getPrimaryPrincipal();return oauthServer.getAuthorizationInfo(username);}
}
六、Shiro vs Spring Security 对比
特性Apache ShiroSpring Security
学习曲线平缓,API简洁陡峭,概念复杂
依赖关系无强制依赖强依赖Spring框架
配置方式INI/Java Config/XMLJava Config/XML
扩展性模块化设计,易扩展扩展需要理解复杂接口
会话管理原生支持需要额外集成
分布式支持内置Redis会话管理需要Spring Session
RESTful支持需自定义实现原生支持良好
社区生态活跃但规模较小企业级广泛支持
微服务集成轻量级,适合微服务功能完备但较重

适用场景建议

  1. 选择 Shiro 当

    • 需要轻量级安全解决方案
    • 非Spring项目或微服务架构
    • 快速实现基础认证/授权
    • 需要灵活的自定义扩展
  2. 选择 Spring Security 当

    • 基于Spring Boot的完整生态
    • 需要OAuth2/OIDC等高级协议
    • 复杂的企业级安全需求
    • 与Spring Cloud深度集成
七、性能优化建议
  1. 权限缓存配置
@Bean
public CacheManager cacheManager() {RedisCacheManager cacheManager = new RedisCacheManager();cacheManager.setRedisManager(redisManager());cacheManager.setExpire(1800); // 缓存30分钟return cacheManager;
}@Bean
public Realm realm() {DatabaseRealm realm = new DatabaseRealm();realm.setCacheManager(cacheManager());realm.setCachingEnabled(true);realm.setAuthenticationCachingEnabled(true);realm.setAuthorizationCachingEnabled(true);return realm;
}
  1. 集群部署优化
# Shiro 集群配置
shiro:session:mode: REDISredis:host: redis-clustertimeout: 2000mscache:type: REDISredis:expire: 30m
  1. 权限验证性能指标
    操作无缓存(ms)有缓存(ms)认证请求45±128±3权限验证32±85±2角色验证28±74±1 \begin{array}{|c|c|c|} \hline \text{操作} & \text{无缓存(ms)} & \text{有缓存(ms)} \\ \hline \text{认证请求} & 45 \pm 12 & 8 \pm 3 \\ \hline \text{权限验证} & 32 \pm 8 & 5 \pm 2 \\ \hline \text{角色验证} & 28 \pm 7 & 4 \pm 1 \\ \hline \end{array} 操作认证请求权限验证角色验证无缓存(ms)45±1232±828±7有缓存(ms)8±35±24±1
总结

Apache Shiro 通过其模块化架构提供灵活的安全解决方案:

  1. 基于 Realm 的数据库权限管理简化了数据源集成
  2. 分布式会话通过 SessionDAO 实现无缝集群扩展
  3. Subject 线程绑定机制确保线程安全访问
  4. ABAC 扩展支持细粒度的属性级权限控制
  5. OAuth2 集成满足现代应用认证需求

相比 Spring Security,Shiro 在轻量级场景和微服务架构中更具优势,而 Spring Security 则在复杂企业级系统中表现更全面。

思维导图

在这里插入图片描述


http://www.lryc.cn/news/618199.html

相关文章:

  • Elasticsearch JavaScript 客户端「基础配置」全指南(Node/TS)
  • 7 种最佳 DBAN 替代方案,彻底擦除硬盘数据
  • ChatGpt 5系列文章1——编码与智能体
  • Go语言实战案例:使用模板渲染HTML页面
  • Go之封装Http请求和日志
  • mysql登录失败 ERROR1698
  • Elasticsearch Node.js 客户端连接指南(Connecting)
  • 实现一个二维码让 iOS 和 Android 用户自动跳转到对应下载链接
  • java面试题储备4: 谈谈对es的理解
  • 基于跨平台的svg组件编写一个svg编辑器
  • 【狂热算法篇】探寻图论幽径之SPFA算法:图论迷宫里的闪电寻径者(通俗易懂版)
  • 【门诊进销存出入库管理系统】佳易王医疗器械零售进销存软件:门诊进销存怎么操作?系统实操教程 #医药系统进销存
  • 需求分发机制如何设定
  • 飞算 JavaAI 电商零售场景实践:从订单峰值到供应链协同的全链路技术革新
  • 元器件--自恢复保险丝
  • 疏老师-python训练营-Day43复习日
  • 基于大数据的在线教育评估系统 Python+Django+Vue.js
  • 【代码随想录day 18】 力扣 501.二叉搜索树中的众数
  • 我的 LeetCode 日记:Day 35 - 解构动态规划,初探 0/1 背包问题
  • 如何检查pip版本
  • Spring Boot项目中调用第三方接口
  • 【Unity】GraphicRaycaster点击失效问题
  • 邦纳BANNER相机视觉加镜头PresencePLUSP4 RICOH FL-CC2514-2M工业相机
  • 一周学会Matplotlib3 Python 数据可视化-绘制饼状图(Pie)
  • 【Activiti】要点初探
  • SQL tutorials
  • 当 GitHub 宕机时,我们如何协作?
  • 【C#】正则表达式
  • 计算机视觉(4)-相机基础知识恶补
  • 计算机网络2-3:传输方式