首页
/ RuoYi-Vue 安全合规改造:从风险诊断到等保三级落地实践

RuoYi-Vue 安全合规改造:从风险诊断到等保三级落地实践

2026-03-07 06:19:12作者:胡唯隽

问题诊断:企业级应用的安全痛点剖析

在数字化转型加速的今天,企业信息系统面临着日益复杂的安全威胁。基于SpringBoot与Vue构建的RuoYi-Vue权限管理系统作为众多企业的基础支撑平台,其安全合规性直接关系到核心业务数据的保护。通过对现有系统的深度审计,我们发现以下关键安全风险点:

身份认证机制存在短板,默认2小时的JWT令牌有效期过长,且缺乏动态刷新机制;权限控制颗粒度不足,按钮级操作权限未完全独立配置;敏感数据在传输和存储环节未进行严格加密;审计日志留存周期不足6个月,难以满足等保三级的追溯要求;定时任务缺乏精细化的权限校验和操作审计。这些隐患如同不设防的门户,随时可能导致数据泄露、越权操作等严重安全事件。

RuoYi-Vue系统登录界面背景图

解决方案:构建全方位安全防护体系

身份认证与访问控制强化

风险点识别(风险等级:高,实施优先级:P0)

  • 密码策略强度不足,易遭受暴力破解
  • JWT令牌有效期过长,增加被盗用风险
  • 缺乏会话超时自动登出机制

技术实现

路径一:密码策略增强 在用户管理服务实现类中添加密码强度检测逻辑,确保密码复杂度符合等保要求:

// 文件路径:ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
private void validatePasswordStrength(String password) {
    // 密码长度至少8位
    if (password.length() < 8) {
        throw new UserException("密码长度不能少于8位");
    }
    // 包含大小写字母、数字及特殊符号
    if (!password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$")) {
        throw new UserException("密码必须包含大小写字母、数字和特殊符号");
    }
}

路径二:会话管理优化 扩展登录用户模型,实现令牌自动刷新机制:

// 文件路径:ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
private Long tokenExpireTime;

// 检查是否需要刷新令牌
public boolean isTokenExpiring() {
    long currentTime = System.currentTimeMillis();
    // 剩余5分钟时触发刷新
    return (tokenExpireTime - currentTime) < 300000;
}

在令牌服务中添加刷新逻辑:

// 文件路径:ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
public String refreshToken(LoginUser loginUser) {
    if (loginUser.isTokenExpiring()) {
        // 生成新令牌
        String newToken = createToken(loginUser);
        // 更新缓存
        redisCache.setCacheObject(loginUser.getTokenKey(), loginUser, getExpireTime(), TimeUnit.MINUTES);
        return newToken;
    }
    return loginUser.getToken();
}

效果验证

  1. 尝试创建不符合复杂度要求的密码,系统应拒绝并提示具体规则
  2. 令牌有效期设置为30分钟,闲置25分钟后执行操作应自动刷新令牌
  3. 超过30分钟未操作,系统应提示会话超时并强制登出

行业最佳实践

金融行业通常采用"双因素认证+动态令牌"机制,如银行系统在密码基础上增加U盾或手机验证码。建议对管理员账户额外启用二次验证,可集成企业微信或钉钉的扫码登录功能。

权限管理精细化改造

风险点识别(风险等级:中,实施优先级:P1)

  • 权限控制未细化到按钮级别
  • 数据权限分离不彻底,存在越权访问风险
  • 缺乏权限继承机制,角色管理复杂

技术实现

路径一:按钮级权限控制 在角色管理页面添加按钮权限配置功能:

<!-- 文件路径:ruoyi-ui/src/views/system/role/index.vue -->
<el-table-column label="操作权限">
  <template slot-scope="scope">
    <el-popover
      placement="right"
      width="400"
      trigger="click">
      <el-tree
        :data="buttonPermissions"
        show-checkbox
        node-key="id"
        :default-checked-keys="scope.row.buttonPerms"
        @check-change="handleButtonPermChange(scope.row, $event)">
      </el-tree>
      <el-button slot="reference" type="text" size="small">配置</el-button>
    </el-popover>
  </template>
</el-table-column>

路径二:数据权限动态控制 增强数据权限切面,支持更细粒度的权限控制:

// 文件路径:ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
private String getScopeSql(SysUser user) {
    StringBuilder sqlString = new StringBuilder();
    
    // 数据权限范围:全部、自定义、本部门、本部门及以下、仅本人
    if (!user.isAdmin()) {
        sqlString.append(" AND (");
        
        // 自定义数据权限
        List<Long> deptIds = user.getDeptIds();
        if (CollUtil.isNotEmpty(deptIds)) {
            sqlString.append(" d.dept_id IN (").append(StringUtils.join(deptIds, ",")).append(")");
        }
        
        // 本部门数据权限
        if (user.getDataScope().equals("1")) {
            sqlString.append(" OR d.dept_id = ").append(user.getDeptId());
        }
        
        // 本部门及以下数据权限
        if (user.getDataScope().equals("2")) {
            sqlString.append(" OR d.dept_id IN (SELECT dept_id FROM sys_dept WHERE ancestors LIKE '%" + user.getDeptId() + "%')");
        }
        
        // 仅本人数据权限
        if (user.getDataScope().equals("3")) {
            sqlString.append(" OR u.user_id = ").append(user.getUserId());
        }
        
        sqlString.append(")");
    }
    
    return sqlString.toString();
}

效果验证

  1. 创建测试角色并配置部分按钮权限,使用该角色登录系统,验证未授权按钮是否隐藏
  2. 配置不同数据权限范围的用户,验证其只能访问权限范围内的数据
  3. 创建子角色继承父角色权限,验证权限继承功能是否正常

行业最佳实践

大型企业通常采用"RBAC+ABAC"混合权限模型,即基于角色的访问控制结合基于属性的访问控制。例如电商平台可根据用户等级、订单金额等属性动态调整操作权限,建议在后续版本中引入此机制。

安全审计与日志管理

风险点识别(风险等级:高,实施优先级:P0)

  • 审计日志记录不完整,关键操作缺乏记录
  • 日志存储周期不足,无法满足等保6个月要求
  • 缺乏日志防篡改机制

技术实现

路径一:全面日志采集 增强操作日志注解,确保所有关键操作被记录:

// 文件路径:ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    /** 模块 */
    String title() default "";
    
    /** 功能 */
    BusinessType businessType() default BusinessType.OTHER;
    
    /** 是否保存请求的参数 */
    boolean saveRequestData() default true;
    
    /** 是否保存响应的参数 */
    boolean saveResponseData() default true;
    
    /** 操作人类别 */
    OperatorType operatorType() default OperatorType.MANAGE;
    
    /** 是否异步记录日志 */
    boolean isAsync() default false;
}

路径二:日志安全存储 实现日志定时备份功能:

// 文件路径:ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
public void backupOperLog() {
    // 创建备份表
    String backupTableName = "sys_oper_log_" + DateUtils.format(new Date(), "yyyyMMdd");
    String createTableSql = "CREATE TABLE " + backupTableName + " LIKE sys_oper_log";
    jdbcTemplate.execute(createTableSql);
    
    // 迁移数据
    String insertSql = "INSERT INTO " + backupTableName + " SELECT * FROM sys_oper_log WHERE oper_time < DATE_SUB(NOW(), INTERVAL 1 DAY)";
    jdbcTemplate.execute(insertSql);
    
    // 删除原表数据
    String deleteSql = "DELETE FROM sys_oper_log WHERE oper_time < DATE_SUB(NOW(), INTERVAL 1 DAY)";
    jdbcTemplate.execute(deleteSql);
    
    // 保留180天备份
    String dropSql = "DROP TABLE IF EXISTS sys_oper_log_" + DateUtils.format(DateUtils.addDays(new Date(), -180), "yyyyMMdd");
    jdbcTemplate.execute(dropSql);
}

效果验证

  1. 执行关键操作(如用户创建、角色授权)后,检查操作日志是否完整记录
  2. 验证日志备份功能是否每天执行,备份文件是否保留180天
  3. 尝试修改历史日志记录,验证系统是否能检测并告警

行业最佳实践

金融行业普遍采用"三员分立"制度(系统管理员、安全管理员、审计管理员),日志管理权限严格分离。建议在系统中实现审计日志的只读权限控制,确保日志数据的完整性和真实性。

敏感数据保护措施

风险点识别(风险等级:高,实施优先级:P0)

  • 敏感数据传输未加密
  • 存储敏感信息未加密
  • 缺乏数据脱敏展示机制

技术实现

路径一:传输加密(HTTPS配置)

# 文件路径:ruoyi-admin/src/main/resources/application.yml
server:
  port: 443
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: RuoYi@2023
    key-store-type: PKCS12
    key-alias: ruoyi
    protocol: TLSv1.3
    ciphers: TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256

路径二:存储加密(AES与国密SM4对比实现)

AES加密实现:

// 文件路径:ruoyi-common/src/main/java/com/ruoyi/common/utils/security/SecurityUtils.java
public static String aesEncrypt(String content, String key) {
    try {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, new byte[12]);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec);
        byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.encodeBase64String(encrypted);
    } catch (Exception e) {
        throw new UtilException("AES加密失败", e);
    }
}

国密SM4加密实现:

// 文件路径:ruoyi-common/src/main/java/com/ruoyi/common/utils/security/SecurityUtils.java
public static String sm4Encrypt(String content, String key) {
    try {
        SM4Engine engine = new SM4Engine(SM4Engine.MODE_CBC);
        KeyParameter keyParameter = new KeyParameter(key.getBytes());
        engine.init(true, keyParameter);
        byte[] srcData = content.getBytes(StandardCharsets.UTF_8);
        byte[] cipherData = new byte[engine.getOutputSize(srcData.length)];
        int len = engine.processBytes(srcData, 0, srcData.length, cipherData, 0);
        engine.doFinal(cipherData, len);
        return Base64.encodeBase64String(cipherData);
    } catch (Exception e) {
        throw new UtilException("SM4加密失败", e);
    }
}

效果验证

  1. 访问系统验证是否自动跳转至HTTPS
  2. 检查数据库中敏感字段(如身份证号、手机号)是否加密存储
  3. 在前端页面验证敏感信息是否脱敏展示(如手机号显示为138****5678)

行业最佳实践

金融和政务领域普遍要求使用国密算法(SM2/SM3/SM4)进行数据加密。建议对涉及国家秘密、商业秘密的系统优先采用国密算法,普通企业可根据合规要求选择AES或国密算法。

定时任务安全管控

风险点识别(风险等级:中,实施优先级:P1)

  • 定时任务缺乏权限控制
  • 任务执行过程无审计记录
  • 任务异常缺乏告警机制

技术实现

路径一:任务权限控制

// 文件路径:ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java
private String permission; // 任务操作权限标识

// 文件路径:ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java
@Override
public void validateJobPermission(SysJob job) {
    if (StringUtils.isNotEmpty(job.getPermission()) 
        && !SecurityUtils.hasPermi(job.getPermission())) {
        throw new AccessDeniedException("无权限操作定时任务:" + job.getJobName());
    }
}

路径二:任务执行审计

// 文件路径:ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java
public interface SysJobLogMapper {
    /**
     * 新增任务日志
     */
    int insertJobLog(SysJobLog jobLog);
    
    /**
     * 查询任务日志详情
     */
    SysJobLog selectJobLogById(Long jobLogId);
    
    /**
     * 查询任务日志列表
     */
    List<SysJobLog> selectJobLogList(SysJobLog jobLog);
}

效果验证

  1. 使用无权限用户尝试执行定时任务,验证是否被拒绝
  2. 执行定时任务后,检查任务日志是否完整记录执行信息
  3. 故意配置错误的任务参数,验证系统是否记录异常信息并告警

行业最佳实践

大型企业通常采用专门的任务调度平台(如XXL-Job、Elastic-Job)管理定时任务,提供更完善的权限控制、监控告警和容错机制。建议对核心业务定时任务考虑接入专业调度平台。

实施验证:构建安全合规的闭环体系

实施计划

第一阶段:准备阶段(1周)

  • 责任人:系统架构师
  • 任务1:梳理现有系统权限体系,输出权限矩阵表
  • 任务2:制定详细改造计划和测试方案
  • 任务3:准备加密所需的密钥和证书

第二阶段:核心改造(3周)

  • 责任人:后端开发工程师、前端开发工程师
  • 任务1:身份认证与权限控制模块改造(1周)
  • 任务2:日志审计与敏感数据保护改造(1周)
  • 任务3:定时任务安全管控改造(0.5周)
  • 任务4:系统集成与联调(0.5周)

第三阶段:测试验证(2周)

  • 责任人:测试工程师、安全工程师
  • 任务1:功能测试(0.5周)
  • 任务2:安全渗透测试(1周)
  • 任务3:性能测试与优化(0.5周)

第四阶段:部署上线(1周)

  • 责任人:运维工程师
  • 任务1:生产环境准备与配置
  • 任务2:数据迁移与系统部署
  • 任务3:上线后监控与问题修复

常见问题排查

问题1:令牌刷新机制失效

  • 现象:用户操作过程中突然提示会话超时
  • 排查步骤:
    1. 检查Redis缓存中登录用户信息是否过期
    2. 验证前端请求是否正确携带令牌
    3. 查看令牌刷新逻辑是否正确处理异常情况
  • 解决方案:调整令牌刷新时机,确保在令牌过期前5分钟触发刷新

问题2:数据权限过滤异常

  • 现象:用户可以看到无权限的数据
  • 排查步骤:
    1. 检查用户数据权限配置是否正确
    2. 调试DataScopeAspect查看生成的SQL条件是否正确
    3. 验证数据库中部门关系数据是否准确
  • 解决方案:修复数据权限SQL生成逻辑,增加权限过滤日志输出

问题3:敏感数据加密后查询异常

  • 现象:加密存储后无法通过明文条件查询数据
  • 排查步骤:
    1. 检查查询条件是否进行了正确加密
    2. 验证加密算法前后一致性
    3. 确认数据库字段长度是否足够存储加密后的数据
  • 解决方案:实现基于加密字段的模糊查询功能,或采用部分加密方案

问题4:定时任务执行日志不完整

  • 现象:部分定时任务执行后无日志记录
  • 排查步骤:
    1. 检查任务执行是否抛出未捕获异常
    2. 验证日志记录逻辑是否在所有执行路径都被调用
    3. 查看数据库连接是否正常
  • 解决方案:增加全局异常捕获,确保日志记录逻辑可靠执行

问题5:HTTPS配置后系统无法访问

  • 现象:配置HTTPS后浏览器提示证书错误或无法连接
  • 排查步骤:
    1. 检查证书是否正确配置
    2. 验证端口是否开放且未被占用
    3. 确认SSL协议和密码套件是否配置正确
  • 解决方案:使用正规CA签发的证书,确保服务器时间与证书有效期匹配

安全合规检查清单

  1. 身份认证

    • [ ] 密码长度不少于8位,包含大小写字母、数字和特殊符号
    • [ ] 实现90天密码定期更换机制
    • [ ] 禁止使用前5次历史密码
    • [ ] JWT令牌有效期设置为30分钟
    • [ ] 实现令牌自动刷新机制
  2. 访问控制

    • [ ] 所有操作按钮配置独立权限标识
    • [ ] 实现基于角色的数据权限控制
    • [ ] 支持数据权限范围:全部、自定义、本部门、本部门及以下、仅本人
    • [ ] 关键操作需二次确认
  3. 安全审计

    • [ ] 记录所有用户关键操作,包含操作人、IP、时间、参数、结果
    • [ ] 审计日志保存至少180天
    • [ ] 实现日志防篡改机制
    • [ ] 敏感操作添加双人复核机制
  4. 数据保护

    • [ ] 所有HTTP通信采用HTTPS加密
    • [ ] 敏感数据存储采用AES或国密SM4加密
    • [ ] 前端展示敏感信息进行脱敏处理
    • [ ] 实现数据备份与恢复机制
  5. 定时任务

    • [ ] 定时任务配置独立操作权限
    • [ ] 记录任务执行日志,包含执行人、参数、结果
    • [ ] 任务异常自动告警
    • [ ] 关键任务执行前需权限验证

通过以上改造,RuoYi-Vue系统将全面满足《信息安全技术 网络安全等级保护基本要求》(GB/T 22239-2019)三级标准,构建起从身份认证、权限控制到数据保护的完整安全防线。建议企业建立常态化安全合规检查机制,每季度进行一次安全评估,确保系统安全状态持续符合要求。

登录后查看全文
热门项目推荐
相关项目推荐