【RuoYi-SpringBoot3-Pro】: 三级等保安全配置

2025博客之星年度评选已开启 10w+人浏览 2.6k人参与

【RuoYi-SpringBoot3-Pro】: 三级等保安全配置

本文详细介绍 RuoYi-SpringBoot3-Pro 框架中内置的三级等保安全策略,帮助企业快速满足国家信息安全等级保护三级要求。

GitHub:https://github.com/undsky/RuoYi-SpringBoot3-Pro

点击获取最新AI资讯、n8n工作流、开发经验分享

一、什么是三级等保?

信息安全等级保护(简称"等保")是我国信息安全保障的基本制度。根据《网络安全法》要求,网络运营者应当按照网络安全等级保护制度的要求,履行安全保护义务。

三级等保适用于:

  • 涉及国家安全、社会秩序、公共利益的重要信息系统
  • 政府机关、金融、能源、交通等关键行业系统
  • 一旦遭到破坏会对社会秩序和公共利益造成严重损害的系统

1.1 等保三级对身份鉴别的要求

要求项具体内容
身份标识唯一性应对登录的用户进行身份标识和鉴别,身份标识具有唯一性
登录失败处理应具有登录失败处理功能,应配置并启用结束会话、限制非法登录次数和当登录连接超时自动退出等相关措施
口令复杂度应采用口令、密码技术、生物技术等两种或两种以上组合的鉴别技术对用户进行身份鉴别
口令更换周期应强制用户首次登录时修改初始口令,并定期更换口令
会话超时当用户在一段时间内无操作时,应自动结束会话

二、RuoYi-SpringBoot3-Pro 等保方案

RuoYi-SpringBoot3-Pro 内置了完善的三级等保安全策略,所有配置通过系统参数表动态管理,无需重启服务即可生效。

2.1 安全策略总览

┌─────────────────────────────────────────────────────────────┐
│                    三级等保安全策略                           │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ 密码更新周期 │  │ 登录失败锁定 │  │ 初始密码修改 │         │
│  │ 90天强制改密 │  │ 5次失败锁定 │  │ 首次登录改密 │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  IP 黑名单  │  │ 无操作登出  │  │ Redis 缓存  │         │
│  │ 通配符/网段 │  │ 前端计时器  │  │ 高性能支持  │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

2.2 系统参数配置表

参数键名参数说明示例值默认值
sys.account.passwordValidateDays密码有效期(天)900(不限制)
sys.account.tryLoginCount登录失败锁定策略5-300(不限制)
sys.account.initPasswordModify初始密码强制修改10(关闭)
sys.login.blackIPListIP 黑名单192.168.1.*;10.0.0.0/8

三、密码更新周期

3.1 功能说明

强制用户定期更换密码,防止密码长期不变带来的安全风险。

3.2 配置方式

在系统管理 → 参数设置中配置:

参数名称:用户管理-账号密码更新周期
参数键名:sys.account.passwordValidateDays
参数键值:90  (90天强制修改密码,0表示不限制)

3.3 实现原理

数据库字段:

用户表 sys_user 中新增 pwd_update_date 字段,记录密码最后更新时间。

ALTER TABLE sys_user ADD COLUMN pwd_update_date datetime COMMENT '密码最后更新时间';

后端校验逻辑:

// SysLoginController.java
public boolean passwordIsExpiration(Date pwdUpdateDate) {
    // 获取密码有效期配置
    Integer passwordValidateDays = Convert.toInt(
        configService.selectConfigByKey("sys.account.passwordValidateDays")
    );
    
    if (passwordValidateDays != null && passwordValidateDays > 0) {
        if (StringUtils.isNull(pwdUpdateDate)) {
            // 从未修改过初始密码,直接提醒过期
            return true;
        }
        Date nowDate = DateUtils.getNowDate();
        // 计算密码使用天数是否超过有效期
        return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
    }
    return false;
}

前端处理:

登录成功后,前端根据 isPasswordExpired 标识判断是否需要强制修改密码:

// 获取用户信息
getInfo().then(res => {
    if (res.isPasswordExpired) {
        // 跳转到修改密码页面
        router.push('/user/profile');
        ElMessage.warning('您的密码已过期,请修改密码');
    }
});

3.4 效果展示

  • 密码超过 90 天未更新 → 登录后强制跳转修改密码页面
  • 修改密码后自动更新 pwd_update_date 字段
  • 配置为 0 时关闭此功能

四、登录失败锁定

4.1 功能说明

防止暴力破解密码,连续多次登录失败后自动锁定账号。

4.2 配置方式

参数名称:用户管理-账号密码尝试登录次数
参数键名:sys.account.tryLoginCount
参数键值:5-30  (5次失败后锁定30分钟,格式:次数-锁定时长)

配置格式说明:

  • 5-30:5 次失败后锁定 30 分钟
  • 3-60:3 次失败后锁定 60 分钟
  • 0:关闭此功能

4.3 实现原理

数据库字段:

用户表 sys_user 中新增 try_count 字段,记录连续登录失败次数。

ALTER TABLE sys_user ADD COLUMN try_count int DEFAULT 0 COMMENT '尝试登录次数';

后端校验逻辑:

// SysLoginController.java

// 1. 登录前检查是否被锁定
public String ifLockUser(String username) {
    Integer[] configs = getTryLoginCountConfig();
    if (configs == null || configs[1] == null || configs[1] == 0) {
        return null;
    }

    String tryGtCountUsername = username + "-tryGtCount";
    if (redisCache.hasKey(tryGtCountUsername)) {
        String datetime = redisCache.getCacheObject(tryGtCountUsername);
        long betweenMinute = DateUtil.between(DateUtil.parse(datetime), new Date(), DateUnit.MINUTE);
        if (betweenMinute > configs[1]) {
            // 锁定时间已过,自动解锁
            unLockUser(username);
            return null;
        } else {
            return "连续" + configs[0] + "次登录失败,请" + configs[1] + "分钟后再试";
        }
    }
    return null;
}

// 2. 解析配置
public Integer[] getTryLoginCountConfig() {
    String tryLoginCount = configService.selectConfigByKey("sys.account.tryLoginCount");
    if (StringUtils.equals("0", tryLoginCount)) {
        return null;
    }
    String[] arr = StringUtils.split(tryLoginCount, "-");
    if (arr.length == 2) {
        Integer[] configs = new Integer[2];
        configs[0] = Convert.toInt(arr[0]);  // 失败次数
        configs[1] = Convert.toInt(arr[1]);  // 锁定时长(分钟)
        return configs;
    }
    return null;
}

// 3. 登录失败处理
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
    String username = loginBody.getUsername();
    
    // 检查是否被锁定
    String msg = ifLockUser(username);
    if (null != msg) {
        throw new RuntimeException(msg);
    }

    try {
        String token = loginService.login(username, password, code, uuid);
        // 登录成功,重置失败次数
        userService.resetTryCount(username, 0);
        return AjaxResult.success().put(Constants.TOKEN, token);
    } catch (Exception e) {
        SysUser user = userService.selectUserByUserName(username);
        if (null != user && StringUtils.equals("0", user.getDelFlag())) {
            // 累加失败次数
            Integer tryCount = null == user.getTryCount() ? 1 : (user.getTryCount() + 1);
            msg = ifTryGtCount(tryCount);
            if (null != msg) {
                // 达到锁定条件,记录锁定时间到 Redis
                redisCache.setCacheObject(username + "-tryGtCount", DateUtil.now());
            } else {
                // 未达到锁定条件,更新失败次数
                userService.resetTryCount(username, tryCount);
            }
        }
        throw new RuntimeException(msg != null ? msg : e.getMessage());
    }
}

4.4 Redis 缓存设计

使用 Redis 存储锁定状态,提升性能并支持分布式部署:

Key: {username}-tryGtCount
Value: 锁定时间(如 "2024-01-15 10:30:00")

4.5 效果展示

第 1 次失败:用户名或密码错误
第 2 次失败:用户名或密码错误
第 3 次失败:用户名或密码错误
第 4 次失败:用户名或密码错误
第 5 次失败:连续5次登录失败,请30分钟后再试
... 30分钟内无法登录 ...
30分钟后:自动解锁,可以重新登录

五、初始密码强制修改

5.1 功能说明

新用户首次登录时,强制修改初始密码,防止使用默认密码带来的安全风险。

5.2 配置方式

参数名称:用户管理-初始密码修改策略
参数键名:sys.account.initPasswordModify
参数键值:1  (1表示启用,0表示关闭)

5.3 实现原理

// SysLoginController.java
public boolean initPasswordIsModify(Date pwdUpdateDate) {
    Integer initPasswordModify = Convert.toInt(
        configService.selectConfigByKey("sys.account.initPasswordModify")
    );
    // 启用策略 且 从未修改过密码(pwdUpdateDate 为空)
    return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
}

判断逻辑:

  • pwdUpdateDate == null:表示用户从未修改过密码(使用的是管理员分配的初始密码)
  • 用户修改密码后,pwdUpdateDate 会被更新为当前时间

5.4 效果展示

  1. 管理员创建新用户,设置初始密码
  2. 新用户首次登录成功
  3. 系统检测到 pwdUpdateDate 为空
  4. 强制跳转到修改密码页面
  5. 用户修改密码后,pwdUpdateDate 更新
  6. 后续登录不再强制修改

六、IP 黑名单

6.1 功能说明

阻止特定 IP 地址访问系统,支持精确 IP、通配符、网段三种匹配方式。

6.2 配置方式

参数名称:用户登录-黑名单列表
参数键名:sys.login.blackIPList
参数键值:192.168.1.100;192.168.2.*;10.0.0.0/8

配置格式说明:

格式示例说明
精确 IP192.168.1.100匹配单个 IP
通配符192.168.1.*匹配 192.168.1.0 - 192.168.1.255
网段10.0.0.0/8匹配 10.0.0.0 - 10.255.255.255
多个规则用分号 ; 分隔任一规则匹配即拦截

6.3 实现原理

登录前置校验:

// SysLoginService.java
public void loginPreCheck(String username, String password) {
    // ... 其他校验 ...
    
    // IP 黑名单校验
    String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
    if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
        AsyncManager.me().execute(
            AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, 
                MessageUtils.message("login.blocked"))
        );
        throw new BlackListException();
    }
}

IP 匹配工具类:

// IpUtils.java
public static boolean isMatchedIp(String filter, String ip) {
    if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) {
        return false;
    }
    String[] ips = filter.split(";");
    for (String iStr : ips) {
        // 精确匹配
        if (isIP(iStr) && iStr.equals(ip)) {
            return true;
        }
        // 通配符匹配(如 192.168.1.*)
        else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) {
            return true;
        }
        // 网段匹配(如 10.0.0.0/8)
        else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) {
            return true;
        }
    }
    return false;
}

6.4 效果展示

黑名单中的 IP 尝试登录时:

{
    "code": 500,
    "msg": "很抱歉,您的IP已被列入系统黑名单"
}

同时记录登录日志,便于安全审计。

七、用户无操作自动登出

7.1 功能说明

当用户在指定时间内无任何操作时,系统自动登出以保护账户安全。

7.2 配置方式

在前端环境变量文件中配置:

# .env.production
VITE_LOGOUT_LIMIT=1800000  # 30分钟(毫秒)

配置说明:

  • 1800000:30 分钟(30 × 60 × 1000 毫秒)
  • 3600000:60 分钟
  • 0 或不配置:关闭此功能

7.3 实现原理

前端实现(App.vue):

onMounted(() => {
    const logoutLimit = import.meta.env.VITE_LOGOUT_LIMIT;
    if (logoutLimit && logoutLimit > 0) {
        // 设置自动登出计时器
        let logoutTimer = setTimeout(logout, logoutLimit);

        // 用户操作后重置计时器
        let userOpDelay = () => {
            clearTimeout(logoutTimer);
            logoutTimer = setTimeout(logout, logoutLimit);
        };

        // 监听用户操作事件
        document.getElementById('app').addEventListener('keydown', userOpDelay);   // 键盘
        document.getElementById('app').addEventListener('mousemove', userOpDelay); // 鼠标移动
        document.getElementById('app').addEventListener('mousedown', userOpDelay); // 鼠标点击
        document.getElementById('app').addEventListener('click', userOpDelay);     // 点击
        document.getElementById('app').addEventListener('scroll', userOpDelay);    // 滚动
    }
});

function logout() {
    userStore.logOut().then(() => {
        location.href = '/admin/index';
    });
}

7.4 监控的用户操作

事件类型说明
keydown键盘按键
mousemove鼠标移动
mousedown鼠标按下
click鼠标点击
scroll页面滚动

7.5 效果展示

  1. 用户登录系统
  2. 开始 30 分钟倒计时
  3. 用户进行任何操作 → 重置倒计时
  4. 30 分钟内无操作 → 自动登出,跳转登录页

八、安全审计日志

所有安全相关操作都会记录到登录日志表,便于安全审计:

// 记录登录失败日志
AsyncManager.me().execute(
    AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名或密码错误")
);

// 记录登录成功日志
AsyncManager.me().execute(
    AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功")
);

// 记录 IP 黑名单拦截日志
AsyncManager.me().execute(
    AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "IP已被列入黑名单")
);

日志查询:

系统管理 → 日志管理 → 登录日志

字段说明
用户名登录用户名
IP 地址登录 IP
登录状态成功/失败
提示消息详细信息
登录时间操作时间

九、最佳实践

9.1 推荐配置

# 密码有效期:90天
sys.account.passwordValidateDays = 90

# 登录失败锁定:5次失败锁定30分钟
sys.account.tryLoginCount = 5-30

# 初始密码强制修改:启用
sys.account.initPasswordModify = 1

# IP 黑名单:根据实际情况配置
sys.login.blackIPList = 

# 前端无操作超时:30分钟
VITE_LOGOUT_LIMIT = 1800000

9.2 密码复杂度建议

配合密码复杂度校验,建议密码满足:

  • 长度至少 8 位
  • 包含大写字母、小写字母、数字、特殊字符中的至少 3 种
  • 不能与用户名相同
  • 不能与最近 N 次使用的密码相同

9.3 安全加固建议

建议项说明
启用 HTTPS防止密码在传输过程中被窃取
密码加密传输前端使用 RSA 加密密码后传输
验证码启用图形验证码,防止自动化攻击
定期审计定期检查登录日志,发现异常行为
最小权限用户只分配必要的权限

十、常见问题

10.1 忘记密码被锁定怎么办?

管理员可以通过以下方式解锁:

-- 方式一:清除 Redis 缓存
DEL {username}-tryGtCount

-- 方式二:重置数据库失败次数
UPDATE sys_user SET try_count = 0 WHERE user_name = 'xxx';

10.2 如何临时关闭等保策略?

将对应参数值设置为 0 即可:

sys.account.passwordValidateDays = 0
sys.account.tryLoginCount = 0
sys.account.initPasswordModify = 0

10.3 配置修改后需要重启吗?

不需要。所有配置通过系统参数表管理,修改后立即生效。

10.4 如何查看被锁定的用户?

-- 查询 Redis 中的锁定记录
KEYS *-tryGtCount

-- 查询数据库中失败次数较高的用户
SELECT user_name, try_count FROM sys_user WHERE try_count > 0;

十一、总结

RuoYi-SpringBoot3-Pro 的三级等保安全方案具有以下特点:

  • 开箱即用:内置完善的安全策略,无需额外开发
  • 灵活配置:所有策略通过系统参数动态管理
  • 无需重启:配置修改后立即生效
  • 高性能:使用 Redis 缓存,支持分布式部署
  • 完整审计:所有安全操作记录日志,便于审计
  • 前后端协同:后端校验 + 前端超时登出,全方位保护

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

undsky_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值