Java 安全 | 从 Shiro 底层源码看 Shiro 漏洞 (上)
字数 1884 2025-08-20 18:17:59

Apache Shiro 安全框架深度解析与漏洞原理

一、Shiro 框架概述

1.1 权限管理基础概念

权限管理是指根据不同身份的用户登录系统后,展示不同功能模块的能力。例如:

  • 学生登录:选课、成绩查询、课程表
  • 老师登录:学生管理、成绩录入

1.2 权限管理实现方式

1.2.1 基于页面的实现

  • 适用于权限管理简单、用户少的场景
  • 缺点:新增功能需要修改页面,维护成本高

1.2.2 RBAC (基于角色的访问控制)

  • 基本原型:用户-权限直接关联
  • 改进版:引入角色概念,用户-角色-权限
  • 高级版:用户-角色-权限 + 用户-权限(直接分配)

1.3 安全框架的作用

安全框架(如Shiro)相当于系统的"保安",负责:

  • 认证(Authentication):验证用户身份
  • 授权(Authorization):检查用户权限
  • 会话管理(Session Management)
  • 密码管理(Cryptography)

二、Shiro 核心功能与组件

2.1 核心功能

  1. 认证:验证用户身份(登录认证)
  2. 授权:检查用户权限/角色
  3. 会话管理:用户认证后创建会话
  4. 密码管理:敏感信息加密

2.2 支持特性

  • Web支持:通过过滤器拦截请求
  • 缓存支持:缓存用户信息和权限
  • 并发支持
  • 测试支持
  • Remember Me功能

2.3 核心组件与运行流程

Subject (用户) → SecurityManager (安全管理器) → Realm (数据源)
  1. Subject:代表当前用户,由SecurityUtils创建
  2. SecurityManager:Shiro核心,管理所有Subject
  3. Realm:安全数据源,连接Shiro与应用的"桥梁"

三、Shiro 基础使用

3.1 Java SE 环境使用

3.1.1 依赖配置

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.1</version>
</dependency>

3.1.2 IniRealm 配置

创建shiro.ini文件:

[users]
heihuUser=heihuPass,seller
hacker=123456,ckmgr
admin=admin888,admin

[roles]
seller=order-add,order-del,order-list
ckmgr=ck-add,ck-del,ck-list
admin=*

3.1.3 认证代码示例

DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));
SecurityUtils.setSecurityManager(defaultSecurityManager);

Subject subject = SecurityUtils.getSubject();
try {
    subject.login(new UsernamePasswordToken(username, password));
    System.out.println("登陆成功!");
} catch (IncorrectCredentialsException e) {
    System.out.println("登陆失败!");
} catch (UnknownAccountException e) {
    System.out.println("用户名不存在!");
}

3.1.4 授权检查

subject.hasRole("seller"); // 检查角色
subject.isPermitted("order-del"); // 检查权限

3.2 SpringBoot 整合 Shiro

3.2.1 依赖配置

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.1</version>
</dependency>
<!-- 其他SpringBoot相关依赖 -->

3.2.2 Shiro 配置类

@Configuration
public class ShiroAutoConfiguration {
    @Bean
    public IniRealm getIniRealm() {
        return new IniRealm("classpath:shiro.ini");
    }
    
    @Bean
    public SecurityManager getSecurityManager(IniRealm iniRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(iniRealm);
        return manager;
    }
    
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
        factory.setSecurityManager(securityManager);
        
        Map<String,String> filterMap = new HashMap();
        filterMap.put("/", "anon");
        filterMap.put("/login", "anon");
        filterMap.put("/user/login", "anon");
        filterMap.put(authc");
        
        factory.setFilterChainDefinitionMap(filterMap);
        factory.setLoginUrl("/login");
        factory.setUnauthorizedUrl(
        return factory;
    }
}

3.2.3 控制器示例

@Controller
@RequestMapping("/user")
public class UserController {
    @PostMapping("/login")
    public String login(String username, String password) {
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.login(new UsernamePasswordToken(username, password));
            return "index";
        } catch (Exception e) {
            return "login";
        }
    }
}

3.3 JdbcRealm 使用

3.3.1 数据库表结构

CREATE TABLE `users`(
    username varchar(60) primary key,
    password varchar(60) not null
);

CREATE TABLE `user_roles`(
    username varchar(60) not null,
    role_name varchar(100) not null
);

CREATE TABLE `roles_permissions`(
    role_name varchar(100) not null,
    permission varchar(100) not null
);

3.3.2 JdbcRealm 配置

@Bean
public JdbcRealm getJdbcRealm(DataSource dataSource) {
    JdbcRealm jdbcRealm = new JdbcRealm();
    jdbcRealm.setDataSource(dataSource);
    jdbcRealm.setPermissionsLookupEnabled(true); // 开启授权功能
    return jdbcRealm;
}

3.4 Shiro 标签使用

3.4.1 依赖配置

<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.1.0</version>
</dependency>

3.4.2 配置 ShiroDialect

@Bean
public ShiroDialect getShiroDialect() {
    return new ShiroDialect();
}

3.4.3 模板中使用

<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<!-- 游客访问 -->
<shiro:guest>
    欢迎游客访问 | <a href="/login">登录</a>
</shiro:guest>

<!-- 已登录用户 -->
<shiro:user>
    欢迎用户名为: <shiro:principal/> 的用户访问!
    
    <!-- 角色检查 -->
    <shiro:hasRole name="admin">超级管理员</shiro:hasRole>
    
    <!-- 权限检查 -->
    <shiro:hasPermission name="order:add">
        <a href="#">添加订单</a>
    </shiro:hasPermission>
</shiro:user>

四、自定义 Realm 实现

4.1 数据库设计

4.1.1 表结构

-- 用户表
CREATE TABLE `tb_users`(
    user_id int primary key,
    username varchar(60) unique,
    password varchar(60)
);

-- 角色表
CREATE TABLE `tb_roles`(
    role_id int primary key,
    role_name varchar(60)
);

-- 权限表
CREATE TABLE `tb_permissions`(
    permission_id int primary key,
    permission_code varchar(60),
    permission_name varchar(60)
);

-- 用户角色关联表
CREATE TABLE `tb_urs`(
    uid int,
    rid int
);

-- 角色权限关联表
CREATE TABLE `tb_rps`(
    rid int,
    pid int
);

4.1.2 查询语句设计

  • 认证查询:根据用户名查询用户信息
  • 授权查询:
    • 查询用户角色
    • 查询角色权限

4.2 自定义 Realm 实现

public class CustomRealm extends AuthorizingRealm {
    
    @Autowired
    private UserService userService;
    
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        
        // 查询用户角色
        Set<String> roles = userService.findRolesByUsername(username);
        info.setRoles(roles);
        
        // 查询用户权限
        Set<String> permissions = userService.findPermissionsByUsername(username);
        info.setStringPermissions(permissions);
        
        return info;
    }
    
    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 
        throws AuthenticationException {
        
        String username = (String)token.getPrincipal();
        User user = userService.findByUsername(username);
        
        if(user == null) {
            throw new UnknownAccountException("用户不存在");
        }
        
        return new SimpleAuthenticationInfo(
            user.getUsername(),
            user.getPassword(),
            ByteSource.Util.bytes(user.getSalt()), // 加盐
            getName()
        );
    }
}

4.3 密码加密与加盐

// 配置密码匹配器
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("SHA-256"); // 加密算法
    matcher.setHashIterations(1024); // 哈希迭代次数
    matcher.setStoredCredentialsHexEncoded(true); // 十六进制编码
    return matcher;
}

// 在Realm中设置
@Bean
public CustomRealm customRealm() {
    CustomRealm realm = new CustomRealm();
    realm.setCredentialsMatcher(hashedCredentialsMatcher());
    return realm;
}

五、Shiro 漏洞原理分析

5.1 Shiro 记住我功能

Shiro的RememberMe功能实现机制:

  1. 用户登录时选择"记住我"
  2. 服务端生成包含用户信息的加密Cookie
  3. 下次访问时,Shiro自动解密Cookie进行认证

5.2 漏洞成因

5.2.1 加密密钥硬编码

早期版本使用固定加密密钥(硬编码在代码中)

5.2.2 AES加密模式问题

  • 使用ECB模式(电子密码本模式)
  • 相同明文块加密后得到相同密文块
  • 无初始向量(IV),易受攻击

5.2.3 加密数据构造

RememberMe Cookie结构:

Base64(序列化数据 + AES加密(序列化数据))

5.3 攻击流程

  1. 构造恶意序列化数据
  2. 使用已知密钥或暴力破解密钥加密
  3. 替换Cookie中的RememberMe字段
  4. 服务端反序列化时执行恶意代码

5.4 漏洞修复方案

  1. 升级到最新版本
  2. 自定义加密密钥:
@Bean
public DefaultWebSecurityManager securityManager() {
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    manager.setRealm(customRealm());
    
    // 设置RememberMe管理器
    CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
    rememberMeManager.setCipherKey(Base64.decode("自定义Base64编码密钥"));
    manager.setRememberMeManager(rememberMeManager);
    
    return manager;
}
  1. 禁用RememberMe功能(如不需要)
  2. 使用更安全的加密模式(如GCM)

六、Shiro 安全最佳实践

  1. 密钥管理

    • 避免使用默认密钥
    • 定期更换密钥
    • 密钥存储在安全位置
  2. 会话管理

    • 设置合理的会话超时时间
    • 实现会话固定保护
    • 限制并发会话数
  3. 密码安全

    • 使用强哈希算法(如SHA-256、BCrypt)
    • 加盐处理
    • 适当设置哈希迭代次数
  4. 权限设计

    • 遵循最小权限原则
    • 定期审查权限分配
    • 实现权限变更审计
  5. 其他安全措施

    • 启用CSRF防护
    • 实现请求频率限制
    • 记录安全相关事件

七、总结

Apache Shiro是一个功能强大且灵活的安全框架,通过本文我们深入了解了:

  1. Shiro的核心架构和运行原理
  2. 多种集成方式(Java SE、SpringBoot)
  3. 不同Realm的实现(Ini、JDBC、自定义)
  4. 权限控制和标签使用
  5. 安全漏洞的底层原理
  6. 安全加固的最佳实践

正确配置和使用Shiro可以显著提高应用的安全性,而不当的配置则可能引入严重的安全隐患。开发者应当深入理解其工作原理,遵循安全最佳实践,才能充分发挥Shiro的安全防护能力。

Apache Shiro 安全框架深度解析与漏洞原理 一、Shiro 框架概述 1.1 权限管理基础概念 权限管理是指根据不同身份的用户登录系统后,展示不同功能模块的能力。例如: 学生登录:选课、成绩查询、课程表 老师登录:学生管理、成绩录入 1.2 权限管理实现方式 1.2.1 基于页面的实现 适用于权限管理简单、用户少的场景 缺点:新增功能需要修改页面,维护成本高 1.2.2 RBAC (基于角色的访问控制) 基本原型:用户-权限直接关联 改进版:引入角色概念,用户-角色-权限 高级版:用户-角色-权限 + 用户-权限(直接分配) 1.3 安全框架的作用 安全框架(如Shiro)相当于系统的"保安",负责: 认证(Authentication):验证用户身份 授权(Authorization):检查用户权限 会话管理(Session Management) 密码管理(Cryptography) 二、Shiro 核心功能与组件 2.1 核心功能 认证 :验证用户身份(登录认证) 授权 :检查用户权限/角色 会话管理 :用户认证后创建会话 密码管理 :敏感信息加密 2.2 支持特性 Web支持:通过过滤器拦截请求 缓存支持:缓存用户信息和权限 并发支持 测试支持 Remember Me功能 2.3 核心组件与运行流程 Subject :代表当前用户,由SecurityUtils创建 SecurityManager :Shiro核心,管理所有Subject Realm :安全数据源,连接Shiro与应用的"桥梁" 三、Shiro 基础使用 3.1 Java SE 环境使用 3.1.1 依赖配置 3.1.2 IniRealm 配置 创建 shiro.ini 文件: 3.1.3 认证代码示例 3.1.4 授权检查 3.2 SpringBoot 整合 Shiro 3.2.1 依赖配置 3.2.2 Shiro 配置类 3.2.3 控制器示例 3.3 JdbcRealm 使用 3.3.1 数据库表结构 3.3.2 JdbcRealm 配置 3.4 Shiro 标签使用 3.4.1 依赖配置 3.4.2 配置 ShiroDialect 3.4.3 模板中使用 四、自定义 Realm 实现 4.1 数据库设计 4.1.1 表结构 4.1.2 查询语句设计 认证查询:根据用户名查询用户信息 授权查询: 查询用户角色 查询角色权限 4.2 自定义 Realm 实现 4.3 密码加密与加盐 五、Shiro 漏洞原理分析 5.1 Shiro 记住我功能 Shiro的RememberMe功能实现机制: 用户登录时选择"记住我" 服务端生成包含用户信息的加密Cookie 下次访问时,Shiro自动解密Cookie进行认证 5.2 漏洞成因 5.2.1 加密密钥硬编码 早期版本使用固定加密密钥(硬编码在代码中) 5.2.2 AES加密模式问题 使用ECB模式(电子密码本模式) 相同明文块加密后得到相同密文块 无初始向量(IV),易受攻击 5.2.3 加密数据构造 RememberMe Cookie结构: 5.3 攻击流程 构造恶意序列化数据 使用已知密钥或暴力破解密钥加密 替换Cookie中的RememberMe字段 服务端反序列化时执行恶意代码 5.4 漏洞修复方案 升级到最新版本 自定义加密密钥: 禁用RememberMe功能(如不需要) 使用更安全的加密模式(如GCM) 六、Shiro 安全最佳实践 密钥管理 : 避免使用默认密钥 定期更换密钥 密钥存储在安全位置 会话管理 : 设置合理的会话超时时间 实现会话固定保护 限制并发会话数 密码安全 : 使用强哈希算法(如SHA-256、BCrypt) 加盐处理 适当设置哈希迭代次数 权限设计 : 遵循最小权限原则 定期审查权限分配 实现权限变更审计 其他安全措施 : 启用CSRF防护 实现请求频率限制 记录安全相关事件 七、总结 Apache Shiro是一个功能强大且灵活的安全框架,通过本文我们深入了解了: Shiro的核心架构和运行原理 多种集成方式(Java SE、SpringBoot) 不同Realm的实现(Ini、JDBC、自定义) 权限控制和标签使用 安全漏洞的底层原理 安全加固的最佳实践 正确配置和使用Shiro可以显著提高应用的安全性,而不当的配置则可能引入严重的安全隐患。开发者应当深入理解其工作原理,遵循安全最佳实践,才能充分发挥Shiro的安全防护能力。