提交
This commit is contained in:
1 parent
5fd0042068
commit
1e6dcb164c
94 files changed
+796
-312
No files matched your search
+3
-3
@@ -80,7 +80,7 @@
|
||||
<profiles.active>dev</profiles.active>
|
||||
<logging.level>info</logging.level>
|
||||
<monitor.username>ruoyi</monitor.username>
|
||||
<monitor.password>123456</monitor.password>
|
||||
<monitor.password>HzdyjsMon@Dev#9Kx7mQ2pL</monitor.password>
|
||||
</properties>
|
||||
<activation>
|
||||
<!-- 默认环境 -->
|
||||
@@ -94,7 +94,7 @@
|
||||
<profiles.active>prod-local</profiles.active>
|
||||
<logging.level>warn</logging.level>
|
||||
<monitor.username>ruoyi</monitor.username>
|
||||
<monitor.password>123456</monitor.password>
|
||||
<monitor.password>HzdyjsMon@Prod#TclhloNMWuVjq</monitor.password>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
@@ -103,7 +103,7 @@
|
||||
<profiles.active>prod</profiles.active>
|
||||
<logging.level>warn</logging.level>
|
||||
<monitor.username>ruoyi</monitor.username>
|
||||
<monitor.password>123456</monitor.password>
|
||||
<monitor.password>HzdyjsMon@Prod#TclhloNMWuVjq</monitor.password>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
@@ -80,11 +80,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再打入运行包(含 /demo/mail、/demo/queue 等) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.dromara.common.core.domain.model.SocialLoginBody;
|
||||
import org.dromara.common.core.utils.*;
|
||||
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.ratelimiter.annotation.RateLimiter;
|
||||
import org.dromara.common.ratelimiter.enums.LimitType;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
|
||||
import org.dromara.common.social.config.properties.SocialProperties;
|
||||
@@ -41,6 +43,7 @@ import org.dromara.web.domain.vo.TenantListVo;
|
||||
import org.dromara.web.service.IAuthStrategy;
|
||||
import org.dromara.web.service.SysLoginService;
|
||||
import org.dromara.web.service.SysRegisterService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -73,6 +76,12 @@ public class AuthController {
|
||||
private final ISysClientService clientService;
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
/**
|
||||
* 配置层注册总开关,默认 false(优先于数据库 sys.account.registerUser)
|
||||
*/
|
||||
@Value("${user.register.enabled:false}")
|
||||
private boolean registerEnabled;
|
||||
|
||||
/**
|
||||
* 登录方法
|
||||
*
|
||||
@@ -119,6 +128,7 @@ public class AuthController {
|
||||
@GetMapping("/binding/{source}")
|
||||
public R<String> authBinding(@PathVariable("source") String source,
|
||||
@RequestParam("tenantId") String tenantId, @RequestParam("domain") String domain) {
|
||||
loginService.checkTenant(tenantId);
|
||||
SocialLoginConfigProperties obj = socialProperties.getType().get(source);
|
||||
if (ObjectUtil.isNull(obj)) {
|
||||
return R.fail(source + "平台账号暂不支持");
|
||||
@@ -178,11 +188,16 @@ public class AuthController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
* 用户注册(免登接口;默认关闭,需同时打开配置与数据库开关)
|
||||
*/
|
||||
@ApiEncrypt
|
||||
@RateLimiter(time = 60, count = 5, limitType = LimitType.IP)
|
||||
@PostMapping("/register")
|
||||
public R<Void> register(@Validated @RequestBody RegisterBody user) {
|
||||
// P6:配置层硬关闭,避免仅依赖库开关被后台误开后遭滥用
|
||||
if (!registerEnabled) {
|
||||
return R.fail("当前系统没有开启注册功能!");
|
||||
}
|
||||
// 先校验租户(含格式白名单),禁止脏 tenantId 进入 selectRegisterEnabled 触发 SQL 注入
|
||||
loginService.checkTenant(user.getTenantId());
|
||||
if (!configService.selectRegisterEnabled(user.getTenantId())) {
|
||||
|
||||
@@ -68,9 +68,11 @@ public class CasController {
|
||||
CAS_BASE, CLIENT_ID, CLIENT_SECRET, URLEncoder.encode(code, StandardCharsets.UTF_8.toString()),
|
||||
REDIRECT_URI_ENCODED);
|
||||
|
||||
log.info("请求 CAS accessToken: {}", url);
|
||||
// 禁止打印 client_secret、完整换票 URL、access_token
|
||||
log.info("请求 CAS accessToken: base={}, client_id={}, codeLen={}", CAS_BASE, CLIENT_ID,
|
||||
code != null ? code.length() : 0);
|
||||
String resp = HttpUtil.get(url);
|
||||
log.info("CAS accessToken 响应: {}", resp);
|
||||
log.info("CAS accessToken 响应已收到: respLen={}", resp != null ? resp.length() : 0);
|
||||
|
||||
// 尝试解析 access_token(CAS 可能返回 JSON 或 query-string 风格)
|
||||
String accessToken = null;
|
||||
@@ -96,9 +98,10 @@ public class CasController {
|
||||
// 使用 access_token 请求 profile 接口
|
||||
String profileUrl = String.format("%s/cas/oauth2.0/profile?access_token=%s", CAS_BASE,
|
||||
URLEncoder.encode(accessToken, StandardCharsets.UTF_8.toString()));
|
||||
log.info("请求 CAS profile: {}", profileUrl);
|
||||
// 禁止打印 access_token 与完整 profile
|
||||
log.info("请求 CAS profile: base={}", CAS_BASE);
|
||||
String profileResp = HttpUtil.get(profileUrl);
|
||||
log.info("CAS profile 响应: {}", profileResp);
|
||||
log.info("CAS profile 响应已收到: respLen={}", profileResp != null ? profileResp.length() : 0);
|
||||
|
||||
// 解析 profile,尝试从 attributes 中找到 CODE(用户名) 和 TENANTID
|
||||
try {
|
||||
@@ -178,7 +181,7 @@ public class CasController {
|
||||
return R.fail("处理 CAS profile 失败: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
return R.fail("无法解析 CAS access_token,原始响应: " + resp);
|
||||
return R.fail("无法解析 CAS access_token");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("请求 CAS accessToken 失败", e);
|
||||
|
||||
@@ -225,14 +225,16 @@ public class SysLoginService {
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
public void checkTenant(String tenantId) {
|
||||
// 只要传入了 tenantId,无论租户开关是否开启都先做格式白名单(防注入)
|
||||
if (StringUtils.isNotBlank(tenantId)) {
|
||||
TenantHelper.checkTenantIdFormat(tenantId);
|
||||
}
|
||||
if (!TenantHelper.isEnable()) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(tenantId)) {
|
||||
throw new TenantException("tenant.number.not.blank");
|
||||
}
|
||||
// 格式白名单,阻断恶意 tenantId 进入后续查询/动态租户
|
||||
TenantHelper.checkTenantIdFormat(tenantId);
|
||||
if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.domain.bo.SysUserBo;
|
||||
import org.dromara.system.mapper.SysUserMapper;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@@ -37,11 +38,19 @@ public class SysRegisterService {
|
||||
private final SysUserMapper userMapper;
|
||||
private final CaptchaProperties captchaProperties;
|
||||
|
||||
@Value("${user.register.enabled:false}")
|
||||
private boolean registerEnabled;
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public void register(RegisterBody registerBody) {
|
||||
if (!registerEnabled) {
|
||||
throw new UserException("user.register.disabled");
|
||||
}
|
||||
String tenantId = registerBody.getTenantId();
|
||||
// 动态租户查询前校验格式,防止绕过 Controller 时注入
|
||||
TenantHelper.checkTenantIdFormat(tenantId);
|
||||
String username = registerBody.getUsername();
|
||||
String password = registerBody.getPassword();
|
||||
// 校验用户类型是否存在
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
--- # 监控中心配置
|
||||
--- # Actuator 全关(不使用)
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
|
||||
--- # 监控中心配置(不使用,关闭客户端)
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://localhost:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
@@ -74,8 +86,8 @@ spring:
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
# 增加锁等待超时参数,减少锁冲突
|
||||
url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&innodb_lock_wait_timeout=120&autocommit=true
|
||||
username: root
|
||||
password: hn02le.34lkdLKD
|
||||
username: hzdyjs_app
|
||||
password: Hzdyjs@App#n4SQi8AfPHldNj49
|
||||
# # 从库数据源
|
||||
# slave:
|
||||
# lazy: true
|
||||
@@ -130,7 +142,7 @@ spring.data:
|
||||
# 数据库索引
|
||||
database: 8
|
||||
# redis 密码必须配置
|
||||
password: Hzdyjs@123456
|
||||
password: "Fwd7y-8UDlZ3%3VO8kwn%J1T"
|
||||
# 连接超时时间
|
||||
timeout: 30s
|
||||
# 是否开启ssl
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -18,7 +18,7 @@ logging:
|
||||
|
||||
# 监控中心配置 - 端口漂移
|
||||
spring.boot.admin.client:
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
--- # 临时文件存储位置 避免临时文件被系统清理报错
|
||||
spring.servlet.multipart.location: /ruoyi/server/temp
|
||||
|
||||
--- # 监控中心配置
|
||||
--- # Actuator 全关(不使用)
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
|
||||
--- # 监控中心配置(不使用,关闭客户端)
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://47.121.125.41:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
@@ -53,8 +65,8 @@ spring:
|
||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: hn02le.34lkdLKD
|
||||
username: hzdyjs_app
|
||||
password: Hzdyjs@App#n4SQi8AfPHldNj49
|
||||
# # 从库数据源
|
||||
# slave:
|
||||
# lazy: true
|
||||
@@ -107,7 +119,7 @@ spring.data:
|
||||
# 数据库索引
|
||||
database: 3
|
||||
# redis 密码必须配置
|
||||
password: Hzdyjs@123456
|
||||
password: "Fwd7y-8UDlZ3%3VO8kwn%J1T"
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
# 是否开启ssl
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
--- # 临时文件存储位置 避免临时文件被系统清理报错
|
||||
spring.servlet.multipart.location: /ruoyi/server/temp
|
||||
|
||||
--- # 监控中心配置
|
||||
--- # Actuator 全关(不使用)
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
|
||||
--- # 监控中心配置(不使用,关闭客户端)
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
@@ -53,8 +65,8 @@ spring:
|
||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
url: jdbc:mysql://172.16.0.67:3306/hzdyjs_xg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: Hzdyjs@Root@123456
|
||||
username: hzdyjs_app
|
||||
password: Hzdyjs@App#n4SQi8AfPHldNj49
|
||||
# # 从库数据源
|
||||
# slave:
|
||||
# lazy: true
|
||||
|
||||
@@ -58,6 +58,9 @@ logging:
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
# 开放注册总开关(P6:校园场景默认关闭,防止免登 /auth/register 被滥用)
|
||||
register:
|
||||
enabled: false
|
||||
password:
|
||||
# 密码最大错误次数
|
||||
maxRetryCount: 5
|
||||
@@ -125,7 +128,7 @@ sa-token:
|
||||
|
||||
# security配置
|
||||
security:
|
||||
# 排除路径
|
||||
# 排除路径(P0:已下线 Swagger/api-docs,不再加入白名单)
|
||||
excludes:
|
||||
- /*.html
|
||||
- /**/*.html
|
||||
@@ -133,20 +136,10 @@ security:
|
||||
- /**/*.js
|
||||
- /favicon.ico
|
||||
- /error
|
||||
- /*/api-docs
|
||||
- /*/api-docs/**
|
||||
- /api-docs
|
||||
- /api-docs/**
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
- /swagger-ui/index.html
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
- /warm-flow-ui/config
|
||||
# 招聘门户公开接口 weizhong
|
||||
- /rs/job/public/**
|
||||
- /rs/job/view/**
|
||||
- /rs/application/submit
|
||||
- /system/dict/data/type/*
|
||||
|
||||
# 多租户配置
|
||||
@@ -216,50 +209,25 @@ api-decrypt:
|
||||
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
|
||||
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
|
||||
|
||||
# P0:Swagger / api-docs 已下线,禁止对外暴露接口清单
|
||||
springdoc:
|
||||
api-docs:
|
||||
# 是否开启接口文档
|
||||
enabled: true
|
||||
# 接口文档路径
|
||||
enabled: false
|
||||
path: /api-docs
|
||||
swagger-ui:
|
||||
# 启用 Swagger UI
|
||||
enabled: true
|
||||
# Swagger UI 路径
|
||||
enabled: false
|
||||
path: /swagger-ui.html
|
||||
info:
|
||||
# 标题
|
||||
title: '标题:杭州第一技师学院智慧校园管理系统_接口文档'
|
||||
# 描述
|
||||
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
|
||||
# 版本
|
||||
description: '描述:接口文档已关闭'
|
||||
version: '版本号: ${ruoyi.version}'
|
||||
# 作者信息
|
||||
contact:
|
||||
name: Lion Li
|
||||
email: crazylionli@163.com
|
||||
url: https://gitee.com/dromara/RuoYi-Vue-Plus
|
||||
components:
|
||||
# 鉴权方式配置
|
||||
security-schemes:
|
||||
apiKey:
|
||||
type: APIKEY
|
||||
in: HEADER
|
||||
name: ${sa-token.token-name}
|
||||
#这里定义了两个分组,可定义多个,也可以不定义
|
||||
group-configs:
|
||||
- group: 1.演示模块
|
||||
packages-to-scan: org.dromara.demo
|
||||
- group: 2.通用模块
|
||||
packages-to-scan: org.dromara.web
|
||||
- group: 3.系统模块
|
||||
packages-to-scan: org.dromara.system
|
||||
- group: 4.代码生成模块
|
||||
packages-to-scan: org.dromara.generator
|
||||
- group: 5.工作流模块
|
||||
packages-to-scan: org.dromara.workflow
|
||||
- group: 6.所有业务模块
|
||||
packages-to-scan: org.dromara
|
||||
group-configs: []
|
||||
|
||||
# 防止XSS攻击
|
||||
xss:
|
||||
@@ -286,15 +254,18 @@ lock4j:
|
||||
# 分布式锁的超时时间,默认为 30 秒
|
||||
expire: 30000
|
||||
|
||||
--- # Actuator 监控端点的配置项
|
||||
--- # Actuator:不使用监控,关闭全部 Web 端点(含 heapdump)
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ALWAYS
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
logfile:
|
||||
external-file: ./logs/sys-console.log
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ user.login.success=登录成功
|
||||
user.register.success=注册成功
|
||||
user.register.save.error=保存用户 {0} 失败,注册账号已存在
|
||||
user.register.error=注册失败,请联系系统管理人员
|
||||
user.register.disabled=当前系统没有开启注册功能!
|
||||
user.notfound=请重新登录
|
||||
user.forcelogout=管理员强制退出,请重新登录
|
||||
user.unknown.error=未知错误,请重新登录
|
||||
|
||||
@@ -25,6 +25,7 @@ user.login.success=Login successful
|
||||
user.register.success=Register successful
|
||||
user.register.save.error=Failed to save user {0}, The registered account already exists
|
||||
user.register.error=Register failed, please contact system administrator
|
||||
user.register.disabled=User registration is disabled
|
||||
user.notfound=Please login again
|
||||
user.forcelogout=The administrator is forced to exit,please login again
|
||||
user.unknown.error=Unknown error, please login again
|
||||
|
||||
@@ -25,6 +25,7 @@ user.login.success=登录成功
|
||||
user.register.success=注册成功
|
||||
user.register.save.error=保存用户 {0} 失败,注册账号已存在
|
||||
user.register.error=注册失败,请联系系统管理人员
|
||||
user.register.disabled=当前系统没有开启注册功能!
|
||||
user.notfound=请重新登录
|
||||
user.forcelogout=管理员强制退出,请重新登录
|
||||
user.unknown.error=未知错误,请重新登录
|
||||
|
||||
+2
@@ -3,6 +3,7 @@ package org.dromara.common.core.config;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 程序注解配置
|
||||
@@ -12,6 +13,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@AutoConfiguration
|
||||
@EnableAspectJAutoProxy
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
@EnableScheduling
|
||||
public class ApplicationConfig {
|
||||
|
||||
}
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
@@ -31,8 +32,9 @@ public class LoginBody implements Serializable {
|
||||
private String grantType;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
* 租户ID(仅允许纯数字,防止拼进 SQL 导致注入)
|
||||
*/
|
||||
@Pattern(regexp = "^[0-9]{1,20}$", message = "{tenant.id.invalid}")
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
|
||||
+2
-3
@@ -10,17 +10,16 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 启动定时任务
|
||||
* 启动 SnailJob 客户端
|
||||
* <p>Spring {@code @Scheduled} 已由 {@code ApplicationConfig} 统一开启,不再依赖本配置。</p>
|
||||
*
|
||||
* @author opensnail
|
||||
* @date 2024-05-17
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true")
|
||||
@EnableScheduling
|
||||
@EnableSnailJob
|
||||
public class SnailJobConfig {
|
||||
|
||||
|
||||
+4
-2
@@ -160,11 +160,13 @@ public class TenantHelper {
|
||||
* @param global 是否全局生效
|
||||
*/
|
||||
public static void setDynamic(String tenantId, boolean global) {
|
||||
// 传入非空时一律先校验格式,避免租户开关关闭时仍把脏数据带入后续逻辑
|
||||
if (StringUtils.isNotBlank(tenantId)) {
|
||||
checkTenantIdFormat(tenantId);
|
||||
}
|
||||
if (!isEnable()) {
|
||||
return;
|
||||
}
|
||||
// 动态租户一律做格式白名单校验,覆盖 login/register/config 等所有 TenantHelper.dynamic 入口
|
||||
checkTenantIdFormat(tenantId);
|
||||
if (!LoginHelper.isLogin() || !global) {
|
||||
TEMP_DYNAMIC_TENANT.set(tenantId);
|
||||
return;
|
||||
|
||||
+7
-4
@@ -21,22 +21,25 @@ spring:
|
||||
title: RuoYi-Vue-Plus服务监控中心
|
||||
context-path: /admin
|
||||
|
||||
--- # Actuator 监控端点的配置项
|
||||
--- # Actuator 全关(不使用)
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ALWAYS
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
logfile:
|
||||
external-file: ./logs/ruoyi-monitor-admin.log
|
||||
|
||||
--- # 监控配置
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
# 设置 Spring Boot Admin Server 地址
|
||||
url: http://47.121.125.41:9090/admin
|
||||
instance:
|
||||
|
||||
@@ -23,22 +23,25 @@ spring:
|
||||
# 配置公网访问地址,确保重定向和静态资源路径包含正确的端口号
|
||||
# public-url: http://web.hzdyjsxy.cn:18081/admin
|
||||
|
||||
--- # Actuator 监控端点的配置项
|
||||
--- # Actuator 全关(不使用)
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ALWAYS
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
logfile:
|
||||
external-file: ./logs/ruoyi-monitor-admin.log
|
||||
|
||||
--- # 监控配置
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
# 设置 Spring Boot Admin Server 地址
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
|
||||
@@ -21,22 +21,25 @@ spring:
|
||||
title: RuoYi-Vue-Plus服务监控中心
|
||||
context-path: /admin
|
||||
|
||||
--- # Actuator 监控端点的配置项
|
||||
--- # Actuator 全关(不使用)
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ALWAYS
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
logfile:
|
||||
external-file: ./logs/ruoyi-monitor-admin.log
|
||||
|
||||
--- # 监控配置
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
# 设置 Spring Boot Admin Server 地址
|
||||
url: http://127.0.0.1:9090/admin
|
||||
instance:
|
||||
|
||||
@@ -3,8 +3,8 @@ spring:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: hn02le.34lkdLKD
|
||||
username: hzdyjs_app
|
||||
password: Hzdyjs@App#n4SQi8AfPHldNj49
|
||||
hikari:
|
||||
connection-timeout: 30000
|
||||
validation-timeout: 5000
|
||||
@@ -29,7 +29,7 @@ snail-job:
|
||||
--- # 监控中心配置
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://localhost:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
+3
-3
@@ -3,8 +3,8 @@ spring:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: hn02le.34lkdLKD
|
||||
username: hzdyjs_app
|
||||
password: Hzdyjs@App#n4SQi8AfPHldNj49
|
||||
hikari:
|
||||
connection-timeout: 30000
|
||||
validation-timeout: 5000
|
||||
@@ -29,7 +29,7 @@ snail-job:
|
||||
--- # 监控中心配置
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://47.121.125.41:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -3,8 +3,8 @@ spring:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://172.16.0.67:3306/hzdyjs_xg?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: Hzdyjs@Root@123456
|
||||
username: hzdyjs_app
|
||||
password: Hzdyjs@App#n4SQi8AfPHldNj49
|
||||
hikari:
|
||||
connection-timeout: 30000
|
||||
validation-timeout: 5000
|
||||
@@ -29,7 +29,7 @@ snail-job:
|
||||
--- # 监控中心配置
|
||||
spring.boot.admin.client:
|
||||
# 增加客户端开关
|
||||
enabled: true
|
||||
enabled: false
|
||||
url: http://172.16.0.67:9090/admin
|
||||
instance:
|
||||
service-host-type: IP
|
||||
|
||||
@@ -29,11 +29,14 @@ logging:
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
include: ""
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ALWAYS
|
||||
enabled: false
|
||||
heapdump:
|
||||
enabled: false
|
||||
logfile:
|
||||
external-file: ./logs/ruoyi-snailjob-server/console.log
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
+7
@@ -174,4 +174,11 @@ public interface IHqSatSurveyService {
|
||||
* @param response HTTP响应
|
||||
*/
|
||||
void exportSurveyRawData(Long surveyId, jakarta.servlet.http.HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 将已过期问卷状态更新为已结束(status=2)
|
||||
*
|
||||
* @return 更新条数
|
||||
*/
|
||||
int updateExpiredSurveys();
|
||||
}
|
||||
+19
@@ -6,7 +6,9 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -1741,4 +1743,21 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
|
||||
public java.util.List<java.util.Map<String, Object>> getAnswers() { return answers; }
|
||||
public void setAnswers(java.util.List<java.util.Map<String, Object>> answers) { this.answers = answers; }
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 end_time 已过期且未结束的问卷标记为已结束。
|
||||
* 定时任务无登录上下文,需忽略租户插件,覆盖全部租户。
|
||||
*/
|
||||
@Override
|
||||
public int updateExpiredSurveys() {
|
||||
Date now = new Date();
|
||||
return TenantHelper.ignore(() -> {
|
||||
LambdaUpdateWrapper<HqSatSurvey> updateWrapper = Wrappers.lambdaUpdate();
|
||||
updateWrapper.set(HqSatSurvey::getStatus, "2")
|
||||
.set(HqSatSurvey::getUpdateTime, now)
|
||||
.lt(HqSatSurvey::getEndTime, now)
|
||||
.ne(HqSatSurvey::getStatus, "2");
|
||||
return baseMapper.update(null, updateWrapper);
|
||||
});
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.dromara.hq.task;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.hq.service.IHqSatSurveyService;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 满意度调查问卷过期扫描任务
|
||||
* <p>替代原 SnailJob surveyEndTimeJobExecutor:每小时将 end_time 已过期的问卷 status 更新为 2(已结束)。</p>
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class HqSatSurveyExpireTask {
|
||||
|
||||
private static final String LOCK_KEY = "hq:sat:survey:expire:lock";
|
||||
private static final Duration LOCK_TTL = Duration.ofMinutes(10);
|
||||
|
||||
private final IHqSatSurveyService satSurveyService;
|
||||
|
||||
/**
|
||||
* 每小时整点执行(与原 SnailJob 3600 秒间隔对齐)
|
||||
*/
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
public void updateExpiredSurveys() {
|
||||
boolean locked = RedisUtils.setObjectIfAbsent(LOCK_KEY, "1", LOCK_TTL);
|
||||
if (!locked) {
|
||||
log.debug("满意度调查过期扫描跳过:其他实例正在执行");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
log.info("开始执行满意度调查过期扫描");
|
||||
int updatedCount = satSurveyService.updateExpiredSurveys();
|
||||
log.info("满意度调查过期扫描完成,更新数量: {}", updatedCount);
|
||||
} catch (Exception e) {
|
||||
log.error("满意度调查过期扫描失败", e);
|
||||
} finally {
|
||||
RedisUtils.deleteObject(LOCK_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
@@ -27,12 +27,6 @@
|
||||
<artifactId>ruoyi-common-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 添加spring-jdbc依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package org.dromara.job.snailjob;
|
||||
|
||||
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
|
||||
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
|
||||
import com.aizuda.snailjob.client.model.ExecuteResult;
|
||||
import com.aizuda.snailjob.common.core.util.JsonUtil;
|
||||
import com.aizuda.snailjob.common.log.SnailJobLog;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 满意度调查问卷结束时间扫描任务
|
||||
* 扫描hq_sat_survey表,检查end_time是否已小于当前时间
|
||||
* 若已过期,则将状态(status)更新为2
|
||||
*/
|
||||
@Component
|
||||
@JobExecutor(name = "surveyEndTimeJobExecutor")
|
||||
public class SurveyEndTimeJobExecutor {
|
||||
|
||||
@Autowired
|
||||
private SurveyJobService surveyJobService;
|
||||
|
||||
public ExecuteResult jobExecute(JobArgs jobArgs) {
|
||||
SnailJobLog.LOCAL.info("surveyEndTimeJobExecutor start. jobArgs:{}", JsonUtil.toJsonString(jobArgs));
|
||||
SnailJobLog.REMOTE.info("surveyEndTimeJobExecutor start. jobArgs:{}", JsonUtil.toJsonString(jobArgs));
|
||||
|
||||
try {
|
||||
// 执行扫描和状态更新
|
||||
int updatedCount = surveyJobService.updateExpiredSurveys();
|
||||
|
||||
String resultMsg = "满意度调查过期状态更新完成,更新数量: " + updatedCount;
|
||||
SnailJobLog.LOCAL.info(resultMsg);
|
||||
SnailJobLog.REMOTE.info(resultMsg);
|
||||
|
||||
return ExecuteResult.success(resultMsg);
|
||||
} catch (Exception e) {
|
||||
String errorMsg = "满意度调查过期状态更新失败: " + e.getMessage();
|
||||
SnailJobLog.LOCAL.error(errorMsg, e);
|
||||
SnailJobLog.REMOTE.error(errorMsg, e);
|
||||
|
||||
return ExecuteResult.failure(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package org.dromara.job.snailjob;
|
||||
|
||||
import org.dromara.common.core.enums.FormatsType;
|
||||
import org.dromara.common.core.utils.DateUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 满意度调查问卷结束时间扫描服务
|
||||
* 提供扫描过期调查问卷并更新状态的功能
|
||||
*/
|
||||
@Service
|
||||
public class SurveyJobService {
|
||||
|
||||
/**
|
||||
* 满意度调查问卷表名
|
||||
*/
|
||||
private static final String TABLE_NAME = "hq_sat_survey";
|
||||
|
||||
/**
|
||||
* 已结束状态值
|
||||
*/
|
||||
private static final int STATUS_ENDED = 2;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 更新过期的满意度调查问卷状态
|
||||
* 检查end_time小于当前时间的记录,将status更新为2
|
||||
*
|
||||
* @return 更新的记录数
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int updateExpiredSurveys() {
|
||||
Date now = DateUtils.getNowDate();
|
||||
|
||||
// 构建更新SQL
|
||||
String updateSql = "UPDATE " + TABLE_NAME +
|
||||
" SET status = ?, update_time = ? " +
|
||||
" WHERE end_time < ? AND status != ? AND deleted = 0";
|
||||
|
||||
// 执行更新并返回更新的记录数
|
||||
return jdbcTemplate.update(
|
||||
updateSql,
|
||||
STATUS_ENDED,
|
||||
DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, now),
|
||||
DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, now),
|
||||
STATUS_ENDED
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
+4
-6
@@ -24,8 +24,8 @@ import org.dromara.common.core.utils.DateUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.rs.domain.RsApplication;
|
||||
import org.dromara.rs.service.IRsApplicationService;
|
||||
|
||||
@@ -292,9 +292,8 @@ public class RsApplicationController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交应聘申请(公开接口)
|
||||
* 提交应聘申请(须登录)
|
||||
*/
|
||||
@SaIgnore
|
||||
@Log(title = "提交应聘申请", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/submit")
|
||||
public R<Map<String, Object>> submitApplication(@RequestBody RsApplication application) {
|
||||
@@ -307,9 +306,8 @@ public class RsApplicationController extends BaseController {
|
||||
// 设置申请时间
|
||||
application.setApplyTime(new java.util.Date());
|
||||
|
||||
// 对于匿名用户,applicant_id 设置为 null
|
||||
// 如果将来需要支持已登录用户,可以从 token 中获取用户ID
|
||||
application.setApplicantId(null);
|
||||
// 绑定当前登录用户
|
||||
application.setApplicantId(LoginHelper.getUserId());
|
||||
|
||||
// 根据jobId设置职位标题
|
||||
if (application.getJobId() != null) {
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
|
||||
/**
|
||||
* 人事管理初始化控制器
|
||||
@@ -116,9 +116,9 @@ public class RsInitController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新菜单数据(不需要认证)
|
||||
* 更新菜单数据(须登录且具备菜单编辑权限)
|
||||
*/
|
||||
@SaIgnore
|
||||
@SaCheckPermission("system:menu:edit")
|
||||
@PostMapping("/update-menu")
|
||||
@Log(title = "更新人事管理菜单", businessType = BusinessType.UPDATE)
|
||||
public R<Void> updateMenu() {
|
||||
|
||||
+2
@@ -83,6 +83,8 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
|
||||
*/
|
||||
@Override
|
||||
public boolean selectRegisterEnabled(String tenantId) {
|
||||
// 防御纵深:动态租户查询前再次校验,避免绕过 Controller 时注入
|
||||
TenantHelper.checkTenantIdFormat(tenantId);
|
||||
SysConfig retConfig = TenantHelper.dynamic(tenantId, () -> {
|
||||
return baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>()
|
||||
.eq(SysConfig::getConfigKey, "sys.account.registerUser"));
|
||||
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
@@ -92,11 +92,13 @@
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<!-- P0:demo 模块已下线,不再依赖 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
|
||||
+5
-11
@@ -7,7 +7,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
@@ -103,9 +102,8 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据身份证号和准考证号查询报名信息
|
||||
* 根据身份证号和准考证号查询报名信息(须登录)
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/query")
|
||||
public R<ZsjyEnrollmentInfoVo> queryByIdCardAndExamNumber(
|
||||
@RequestParam(value = "idCard", required = false) String idCard,
|
||||
@@ -115,9 +113,8 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交报名信息(供学生端使用)
|
||||
* 提交报名信息(供学生端使用,须登录)
|
||||
*/
|
||||
@SaIgnore
|
||||
@Log(title = "学生报名", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/submit")
|
||||
@@ -126,9 +123,8 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的报名状态(供学生端使用)
|
||||
* 查询我的报名状态(供学生端使用,须登录)
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/myStatus")
|
||||
public R<ZsjyEnrollmentInfoVo> getMyEnrollmentStatus(
|
||||
@RequestParam("idCard") String idCard,
|
||||
@@ -160,9 +156,8 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学院列表(供移动端公开访问)
|
||||
* 获取学院列表(须登录)
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/colleges")
|
||||
public R<List<ZsjyEnrollmentDeptVo>> getColleges() {
|
||||
try {
|
||||
@@ -174,9 +169,8 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学院ID获取专业列表(供移动端公开访问)
|
||||
* 根据学院ID获取专业列表(须登录)
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/majors/{collegeId}")
|
||||
public R<List<ZsjyEnrollmentDeptVo>> getMajorsByCollegeId(@PathVariable("collegeId") Long collegeId) {
|
||||
try {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
## 文档信息
|
||||
|
||||
- **版本**:1.0.0
|
||||
- **最后更新**:2025-07-01
|
||||
- **版本**:1.0.1
|
||||
- **最后更新**:2026-09-20
|
||||
- **作者**:Cursor AI
|
||||
- **分类**:后勤管理模块
|
||||
|
||||
@@ -11,6 +11,13 @@
|
||||
|
||||
本文档详细说明后勤满意度调查系统的API接口设计,包括问卷管理、题目管理、选项管理、答题记录等功能。系统采用前后端分离架构,接口采用RESTful API风格设计,所有接口均需通过Token进行身份验证。
|
||||
|
||||
### 问卷过期处理(2026-09-20)
|
||||
|
||||
- 原依赖 SnailJob 任务 `surveyEndTimeJob` / `surveyEndTimeJobExecutor` 每小时扫描过期问卷。
|
||||
- 已改为 Spring `@Scheduled`:`HqSatSurveyExpireTask`(cron:`0 0 * * * ?`),将 `hq_sat_survey` 中 `end_time` 已过期且未结束的记录 `status` 更新为 `2`。
|
||||
- 多实例通过 Redis 锁 `hq:sat:survey:expire:lock` 避免重复执行。
|
||||
- 测试库已关闭对应 SnailJob 任务(`job_status=0`),避免与 `@Scheduled` 双跑。
|
||||
|
||||
## 通用响应格式
|
||||
|
||||
### 成功响应
|
||||
|
||||
@@ -649,4 +649,4 @@
|
||||
1. 职位申请提交前需要先保存应聘者信息,获取applicantId
|
||||
2. 职位状态更改会影响前台展示,下线的职位不会在公开列表中展示
|
||||
3. 所有API请求需要在Header中附带Token
|
||||
4. 公开的API(如public/list、public/{jobId})可以通过权限配置允许匿名访问
|
||||
4. 公开的API(如public/list、public/{jobId})可以通过权限配置允许匿名访问;`POST /rs/application/submit` 须登录后调用
|
||||
@@ -331,8 +331,8 @@ DEFAULT_TENANT_ID = 000000
|
||||
|
||||
## 7. 后续优化建议
|
||||
|
||||
1. **配置外置化**:将 CAS 地址、client_id、client_secret、redirect_uri 移至 `application.yml`,避免硬编码
|
||||
2. **密钥安全**:前端不应保留 `CAS_CLIENT_SECRET`(当前未使用但存在代码中,建议删除)
|
||||
1. **配置外置化**:将 CAS 地址、client_id、client_secret、redirect_uri 移至 `application.yml`,避免硬编码(后端 secret 仍待外置)
|
||||
2. **密钥安全**:~~前端不应保留 `CAS_CLIENT_SECRET`~~(已删除;换票仅走后端;CAS 相关日志已脱敏,禁止打印 secret/token/profile)
|
||||
3. **租户映射**:如需支持多租户,可恢复对 CAS `TENANTID` 的解析逻辑
|
||||
4. **用户自动注册**:CAS 用户首次登录时自动创建本地账号(需评估权限与安全策略)
|
||||
5. **移动端接入**:mobile 工程可参考相同 OAuth2 流程实现统一认证登录
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
url: jdbc:mysql://47.99.164.66:3306/hzdyjs_xg
|
||||
username: root
|
||||
password: hn02le.34lkdLKD
|
||||
password: nsyOfyOyoFCr%Brj-k_5PqbW
|
||||
@@ -0,0 +1,27 @@
|
||||
-- 宿舍卫生检查扣分明细表
|
||||
-- 生产现象:提交宿舍卫生检查时报 Table 'hzdyjs_xg.xg_dorm_hygiene_check_deduction' doesn't exist
|
||||
-- 原因:代码在保存主表后会先逻辑删除再写入扣分明细,生产库未建该表,整单事务回滚
|
||||
-- 执行库:hzdyjs_xg
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xg_dorm_hygiene_check_deduction` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键ID',
|
||||
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
|
||||
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
|
||||
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`deleted` tinyint(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
||||
`hygiene_check_id` bigint(20) NOT NULL COMMENT '卫生检查ID,关联xg_dorm_hygiene_check.id',
|
||||
`discipline_type_id` bigint(20) NOT NULL COMMENT '违纪类型ID,关联xskh_discipline_type.id',
|
||||
`discipline_type_name` varchar(100) DEFAULT NULL COMMENT '违纪类型名称',
|
||||
`score` decimal(5,2) NOT NULL DEFAULT '0.00' COMMENT '该项扣分',
|
||||
`sort_order` int(11) DEFAULT '1' COMMENT '排序序号',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_deleted` (`deleted`),
|
||||
KEY `idx_hygiene_check_id` (`hygiene_check_id`),
|
||||
KEY `idx_discipline_type_id` (`discipline_type_id`),
|
||||
KEY `idx_tenant_deleted` (`tenant_id`,`deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宿舍卫生检查扣分明细表';
|
||||
@@ -1,4 +1,4 @@
|
||||
// 移动端新生报名相关API接口
|
||||
// 移动端新生报名相关API接口(须登录,需携带 Token)
|
||||
import { request } from '@/common/request.js'
|
||||
|
||||
/**
|
||||
@@ -6,37 +6,31 @@ import { request } from '@/common/request.js'
|
||||
*/
|
||||
export const enrollmentApi = {
|
||||
/**
|
||||
* 提交报名信息(供学生端使用)
|
||||
* 提交报名信息(供学生端使用,须登录)
|
||||
* @param {Object} data 报名表单数据
|
||||
*/
|
||||
submitApplication: (data) => {
|
||||
return request({
|
||||
url: '/zsjy/enrollment/info/submit',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
data: data
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询我的报名状态(供学生端使用)
|
||||
* 查询我的报名状态(供学生端使用,须登录)
|
||||
* @param {Object} params 查询参数 { idCard?, examNumber? }
|
||||
*/
|
||||
queryMyStatus: (params) => {
|
||||
return request({
|
||||
url: '/zsjy/enrollment/info/myStatus',
|
||||
method: 'GET',
|
||||
params: params,
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
params: params
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据身份证号和准考证号查询报名信息
|
||||
* 根据身份证号和准考证号查询报名信息(须登录)
|
||||
* @param {string} idCard 身份证号
|
||||
* @param {string} examNumber 准考证号
|
||||
*/
|
||||
@@ -47,37 +41,28 @@ export const enrollmentApi = {
|
||||
params: {
|
||||
idCard,
|
||||
examNumber
|
||||
},
|
||||
header: {
|
||||
isToken: false // 不需要登录token
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取学院列表
|
||||
* 获取学院列表(须登录)
|
||||
*/
|
||||
getCollegeList: () => {
|
||||
return request({
|
||||
url: '/zsjy/enrollment/info/colleges',
|
||||
method: 'GET',
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
method: 'GET'
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据学院ID获取专业列表
|
||||
* 根据学院ID获取专业列表(须登录)
|
||||
* @param {string|number} collegeId 学院ID
|
||||
*/
|
||||
getMajorsByCollegeId: (collegeId) => {
|
||||
return request({
|
||||
url: `/zsjy/enrollment/info/majors/${collegeId}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1464,14 +1464,11 @@ export default {
|
||||
submitData.isBoarding = 0;
|
||||
}
|
||||
|
||||
// 调用提交接口
|
||||
// 调用提交接口(须登录,携带 Token)
|
||||
const response = await request({
|
||||
url: '/zsjy/enrollment/info/submit',
|
||||
method: 'POST',
|
||||
data: submitData,
|
||||
header: {
|
||||
isToken: false, // 不需要登录token
|
||||
},
|
||||
});
|
||||
|
||||
uni.showToast({
|
||||
@@ -1564,9 +1561,6 @@ export default {
|
||||
idCard: this.queryData.idCard,
|
||||
examNumber: this.queryData.examNumber,
|
||||
},
|
||||
header: {
|
||||
isToken: false, // 不需要登录token
|
||||
},
|
||||
});
|
||||
|
||||
if (response.data) {
|
||||
|
||||
@@ -245,7 +245,7 @@ const doSocialLogin = (type: string) => {
|
||||
// 固定的 CAS 地址与生产前端地址(按需求写死)
|
||||
const CAS_BASE = 'http://td.hzdyjsxy.cn:19081';
|
||||
const CAS_CLIENT_ID = '0i7tcT5tULqvmmbbV8';
|
||||
const CAS_CLIENT_SECRET = '68c4f7641523452aaf5ef6d0432869f7';
|
||||
// client_secret 仅后端持有,前端禁止携带
|
||||
const FRONTEND_REDIRECT_URI_ENCODED = encodeURIComponent('http://web.hzdyjsxy.cn:18081');
|
||||
const CAS_REDIRECT_KEY = 'cas_login_redirect';
|
||||
/** 单点登录门户域名(Referer 匹配) */
|
||||
@@ -371,7 +371,7 @@ const extractCodeFromRoute = (route: any): string | null => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用 code 去请求 accessToken,并打印返回信息
|
||||
* 使用 code 经后端换取本系统登录令牌(换票密钥仅后端使用)
|
||||
*/
|
||||
const fetchAccessToken = async (code: string) => {
|
||||
try {
|
||||
@@ -396,7 +396,8 @@ const fetchAccessToken = async (code: string) => {
|
||||
body = txt;
|
||||
}
|
||||
}
|
||||
console.log('[Backend -> CAS accessToken response]', { status: res.status, ok: res.ok, body });
|
||||
// 不打印 body(可能含 accessToken)
|
||||
console.log('[CAS login] backend response', { status: res.status, ok: res.ok });
|
||||
|
||||
// 如果后端返回了登录令牌,前端直接用它完成登录后的行为(存 token & 跳转)
|
||||
// 兼容多种返回结构:{ data: { access_token: '...' } } 或 { access_token: '...' } 或 raw LoginVo
|
||||
|
||||
@@ -11,7 +11,7 @@ DB_CONFIG = {
|
||||
"port": 3306,
|
||||
"database": "hzdyjs_xg",
|
||||
"user": "root",
|
||||
"password": "hn02le.34lkdLKD",
|
||||
"password": "nsyOfyOyoFCr%Brj-k_5PqbW",
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ DB_CONFIG = {
|
||||
"port": 3306,
|
||||
"database": "hzdyjs_xg",
|
||||
"user": "root",
|
||||
"password": "hn02le.34lkdLKD",
|
||||
"password": "nsyOfyOyoFCr%Brj-k_5PqbW",
|
||||
"charset": "utf8mb4",
|
||||
"autocommit": False, # 手动控制事务
|
||||
"connect_timeout": 60, # 连接超时时间(秒)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ DB_HOST="47.99.164.66"
|
||||
DB_PORT="3306"
|
||||
DB_NAME="hzdyjs_xg"
|
||||
DB_USER="root"
|
||||
DB_PASS="hn02le.34lkdLKD"
|
||||
DB_PASS="nsyOfyOyoFCr%Brj-k_5PqbW"
|
||||
|
||||
# Script configuration
|
||||
LOG_DIR="${SCRIPT_DIR}/student_class_change"
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ DB_HOST="47.99.164.66"
|
||||
DB_PORT="3306"
|
||||
DB_NAME="hzdyjs_xg"
|
||||
DB_USER="root"
|
||||
DB_PASS="hn02le.34lkdLKD"
|
||||
DB_PASS="nsyOfyOyoFCr%Brj-k_5PqbW"
|
||||
|
||||
# Script configuration
|
||||
LOG_DIR="${SCRIPT_DIR}/student_id_card_refresh"
|
||||
|
||||
@@ -97,7 +97,7 @@ DB_HOST="47.99.164.66" # 数据库主机
|
||||
DB_PORT="3306" # 数据库端口
|
||||
DB_NAME="hzdyjs_xg" # 数据库名
|
||||
DB_USER="root" # 数据库用户
|
||||
DB_PASS="hn02le.34lkdLKD" # 数据库密码
|
||||
DB_PASS="nsyOfyOyoFCr%Brj-k_5PqbW" # 数据库密码
|
||||
|
||||
# Script configuration
|
||||
LOG_DIR="${SCRIPT_DIR}/student_id_card_refresh" # 日志目录
|
||||
|
||||
@@ -11,7 +11,7 @@ DB_CONFIG = {
|
||||
"port": 3306,
|
||||
"database": "hzdyjs_xg",
|
||||
"user": "root",
|
||||
"password": "hn02le.34lkdLKD",
|
||||
"password": "nsyOfyOyoFCr%Brj-k_5PqbW",
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ DB_CONFIG = {
|
||||
"port": 3306,
|
||||
"database": "hzdyjs_xg",
|
||||
"user": "root",
|
||||
"password": "hn02le.34lkdLKD",
|
||||
"password": "nsyOfyOyoFCr%Brj-k_5PqbW",
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ SOURCE_DB_HOST="47.99.164.66"
|
||||
SOURCE_DB_PORT="3306"
|
||||
SOURCE_DB_NAME="hzdyjs_xg"
|
||||
SOURCE_DB_USER="root"
|
||||
SOURCE_DB_PASS="hn02le.34lkdLKD"
|
||||
SOURCE_DB_PASS="nsyOfyOyoFCr%Brj-k_5PqbW"
|
||||
|
||||
# ---------- 目标库(认证中间库,读写) ----------
|
||||
# 研发环境:与源库同库,仅新增中间表
|
||||
@@ -19,7 +19,7 @@ TARGET_DB_HOST="47.99.164.66"
|
||||
TARGET_DB_PORT="3306"
|
||||
TARGET_DB_NAME="hzdyjs_xg"
|
||||
TARGET_DB_USER="root"
|
||||
TARGET_DB_PASS="hn02le.34lkdLKD"
|
||||
TARGET_DB_PASS="nsyOfyOyoFCr%Brj-k_5PqbW"
|
||||
|
||||
# ---------- 同步策略 ----------
|
||||
# 同步模式:full=全量,incremental=增量(按 update_time)
|
||||
|
||||
@@ -11,7 +11,7 @@ DB_CONFIG = {
|
||||
"port": 3306,
|
||||
"database": "hzdyjs_xg",
|
||||
"user": "root",
|
||||
"password": "hn02le.34lkdLKD",
|
||||
"password": "nsyOfyOyoFCr%Brj-k_5PqbW",
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
host = 47.99.164.66
|
||||
port = 3306
|
||||
user = root
|
||||
password = hn02le.34lkdLKD
|
||||
password = nsyOfyOyoFCr%%Brj-k_5PqbW
|
||||
database = hzdyjs_xg
|
||||
|
||||
[api]
|
||||
|
||||
@@ -3,7 +3,7 @@ DB_HOST="47.99.164.66"
|
||||
DB_PORT="3306"
|
||||
DB_NAME="hzdyjs_xg"
|
||||
DB_USER="root"
|
||||
DB_PASS="hn02le.34lkdLKD"
|
||||
DB_PASS="nsyOfyOyoFCr%Brj-k_5PqbW"
|
||||
|
||||
# Script configuration
|
||||
LOG_DIR="${SCRIPT_DIR}/student_status_change"
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
# 20260922 安全扫描修改
|
||||
|
||||
> 针对扫描报告中免登录接口暴露、敏感信息泄露及可写风险的整改记录。
|
||||
> 扫描样例:`GET /prod-api/zsjy/enrollment/info/query?examNumber=123456` 可免登返回身份证、手机号等。
|
||||
|
||||
## 一、本次处理范围
|
||||
|
||||
| 优先级 | 项 | 处理结果 |
|
||||
|--------|----|----------|
|
||||
| P0 | `POST /rs/init/update-menu` 未授权改菜单 | **已整改** |
|
||||
| P0 | Swagger `/api-docs`、`/swagger-ui/**` | **已下线** |
|
||||
| P0 | Demo `/demo/mail/**`、`/demo/queue/delayed/**` | **已下线(运行包不再引入)** |
|
||||
| P0 | 招生 5 个免登接口(含扫描点名 `query`) | **已整改**(须登录) |
|
||||
| P1 | `GET /cas/accessToken` 密钥暴露与日志明文 | **部分整改**(前端去 secret + 日志脱敏;后端 secret 外置待做) |
|
||||
| P1 | `POST /rs/application/submit` 匿名滥投 | **已整改**(须登录) |
|
||||
|
||||
---
|
||||
|
||||
## 二、已完成修改明细
|
||||
|
||||
### 1. P0:`POST /rs/init/update-menu` 须登录
|
||||
|
||||
**问题**:标注 `@SaIgnore`,未登录即可删改 `sys_menu` / `sys_role_menu`。
|
||||
|
||||
**修改**:
|
||||
- 移除 `@SaIgnore`
|
||||
- 增加 `@SaCheckPermission("system:menu:edit")`
|
||||
|
||||
**文件**:
|
||||
- `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsInitController.java`
|
||||
|
||||
**验证**:未带 Token → 401;无 `system:menu:edit` → 无权限。
|
||||
|
||||
---
|
||||
|
||||
### 2. P0:Swagger / api-docs 下线
|
||||
|
||||
**问题**:`security.excludes` 放行文档路径,且 `springdoc` 开启,便于枚举全量接口。
|
||||
|
||||
**修改**:
|
||||
- `springdoc.api-docs.enabled` / `swagger-ui.enabled` → `false`
|
||||
- 从 `security.excludes` 移除 `/api-docs`、`/swagger-ui/**`、`/v3/api-docs/**`、`/webjars/**` 等
|
||||
|
||||
**文件**:
|
||||
- `backend/ruoyi-admin/src/main/resources/application.yml`
|
||||
|
||||
**验证**:访问 `/prod-api/swagger-ui.html`、`/prod-api/api-docs` 不可用。
|
||||
|
||||
---
|
||||
|
||||
### 3. P0:Demo 模块下线
|
||||
|
||||
**问题**:`ruoyi-demo` 打入运行包,邮件、延迟队列等接口带 `@SaIgnore`。
|
||||
|
||||
**修改**:注释掉运行路径上所有 `ruoyi-demo` 依赖(admin + 各业务模块),Controller 不再注册。
|
||||
|
||||
**文件**:
|
||||
- `backend/ruoyi-admin/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-hq/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-rs/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-cw/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-xg/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-zsjy/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-jcbt/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-xskh/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-bzrkh/pom.xml`
|
||||
- `backend/ruoyi-modules/ruoyi-aqbw/pom.xml`
|
||||
|
||||
**说明**:源码目录 `ruoyi-modules/ruoyi-demo` 仍保留,默认构建/部署不加载。
|
||||
|
||||
**验证**:`/prod-api/demo/mail/**`、`/prod-api/demo/queue/delayed/**` → 404。
|
||||
|
||||
---
|
||||
|
||||
### 4. P0:招生报名相关 5 个接口须登录(扫描报告点名)
|
||||
|
||||
**问题**:以下接口标注 `@SaIgnore`,未登录即可访问;其中 `query` 仅凭准考证号即可返回身份证、手机号、住址等敏感信息。
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/zsjy/enrollment/info/query` | 按身份证/准考证查报名 |
|
||||
| POST | `/zsjy/enrollment/info/submit` | 学生提交报名 |
|
||||
| GET | `/zsjy/enrollment/info/myStatus` | 查本人报名状态 |
|
||||
| GET | `/zsjy/enrollment/info/colleges` | 学院列表 |
|
||||
| GET | `/zsjy/enrollment/info/majors/{collegeId}` | 专业列表 |
|
||||
|
||||
**修改**:
|
||||
- 后端:移除上述 5 个方法的 `@SaIgnore`,走统一登录校验
|
||||
- 移动端:去掉 `isToken: false`,请求携带 Token
|
||||
|
||||
**文件**:
|
||||
- `backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyEnrollmentInfoController.java`
|
||||
- `mobile/src/api/enrollment.js`
|
||||
- `mobile/src/pages/enrollment/index.vue`
|
||||
|
||||
**影响**:新生报名页须先登录再查询/提交;未带 Token 访问上述接口返回 401。
|
||||
|
||||
**验证**:
|
||||
- 无 Token 访问 `GET /prod-api/zsjy/enrollment/info/query?examNumber=123456` → 401
|
||||
- 登录后携带 Token 可正常查询/提交
|
||||
|
||||
---
|
||||
|
||||
### 5. P1:CAS 换票 — 前端去 secret + 日志脱敏
|
||||
|
||||
**问题**:
|
||||
- 前端写死 `CAS_CLIENT_SECRET`(虽未参与换票,但会进浏览器包)
|
||||
- 后端日志打印含 secret 的完整 URL、access_token、完整 profile
|
||||
|
||||
**修改**:
|
||||
- 删除前端 `CAS_CLIENT_SECRET`;换票仍只调 `GET /cas/accessToken?code=...`
|
||||
- 前端控制台不再打印含 Token 的响应 body
|
||||
- `CasController` 日志仅保留 base/client_id/长度等非敏感信息;失败响应不再回传 CAS 原始报文
|
||||
|
||||
**文件**:
|
||||
- `web/src/views/login.vue`
|
||||
- `backend/ruoyi-admin/src/main/java/org/dromara/web/controller/CasController.java`
|
||||
- `docs/开发文档/统一身份认证接入说明.md`(同步说明)
|
||||
|
||||
**对登录影响**:无(前端本未使用 secret;日志脱敏不改换票逻辑)。
|
||||
|
||||
**未做(后续建议)**:
|
||||
- 后端 `CLIENT_SECRET` 配置外置 + 向 CAS 轮换密钥
|
||||
- `/cas/accessToken` IP 限流
|
||||
|
||||
---
|
||||
|
||||
### 6. P1:`POST /rs/application/submit` 须登录
|
||||
|
||||
**问题**:`@SaIgnore` + `security.excludes` 放行,可匿名灌简历数据。
|
||||
|
||||
**修改**:
|
||||
- 移除 `@SaIgnore`
|
||||
- 从 `security.excludes` 删除 `/rs/application/submit`
|
||||
- 提交时绑定 `applicantId = LoginHelper.getUserId()`
|
||||
|
||||
**文件**:
|
||||
- `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsApplicationController.java`
|
||||
- `backend/ruoyi-admin/src/main/resources/application.yml`
|
||||
- `docs/开发文档/API/招聘模块API.md`
|
||||
|
||||
**影响**:招聘门户投递须先登录;`/rs/job/public/**` 公开浏览不受影响。
|
||||
|
||||
---
|
||||
|
||||
## 三、部署与验证清单
|
||||
|
||||
1. 重新编译打包后端(demo 依赖变更需完整构建),部署后重启。
|
||||
2. 前端若已发布含 CAS secret 的旧包,需重新构建发布 `web`。
|
||||
3. 移动端报名相关改动需重新发布 `mobile`。
|
||||
4. 抽查:
|
||||
- [ ] `POST /rs/init/update-menu` 无 Token → 401
|
||||
- [ ] `/swagger-ui.html`、`/api-docs` 不可用
|
||||
- [ ] `/demo/mail/**` → 404
|
||||
- [ ] `GET /zsjy/enrollment/info/query?examNumber=123456` 无 Token → 401
|
||||
- [ ] 招生 submit / myStatus / colleges / majors 无 Token → 401
|
||||
- [ ] CAS 统一认证登录仍可用
|
||||
- [ ] `POST /rs/application/submit` 无 Token → 401;登录后可提交
|
||||
|
||||
---
|
||||
|
||||
## 四、相关分项说明(可作附录查阅)
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| `问题整改/P0-rs-init-update-menu免登整改说明.md` | 菜单更新接口 |
|
||||
| `问题整改/P0-Swagger与Demo下线整改说明.md` | Swagger / Demo |
|
||||
| `问题整改/P1-CAS换票密钥与日志脱敏整改说明.md` | CAS |
|
||||
| `问题整改/P1-rs-application-submit须登录整改说明.md` | 应聘提交 |
|
||||
|
||||
---
|
||||
|
||||
**文档日期**:2026-09-22
|
||||
**主题**:安全扫描修改
|
||||
@@ -0,0 +1,30 @@
|
||||
# P0:Swagger / Demo 模块下线整改说明
|
||||
|
||||
## 问题
|
||||
|
||||
1. **Swagger / api-docs 免登暴露**:`security.excludes` 放行 `/api-docs`、`/swagger-ui/**` 等,且 `springdoc.*.enabled=true`,扫描器可枚举全量接口。
|
||||
2. **Demo 模块打入运行包**:`/demo/mail/**`、`/demo/queue/delayed/**` 等带 `@SaIgnore`,存在滥用发信等风险。
|
||||
|
||||
## 整改
|
||||
|
||||
### 1. 关闭并收回 Swagger 白名单
|
||||
|
||||
文件:`backend/ruoyi-admin/src/main/resources/application.yml`
|
||||
|
||||
- `springdoc.api-docs.enabled` → `false`
|
||||
- `springdoc.swagger-ui.enabled` → `false`
|
||||
- 从 `security.excludes` 移除全部 api-docs / swagger-ui / webjars 路径
|
||||
|
||||
### 2. 运行包不再引入 demo
|
||||
|
||||
- `backend/ruoyi-admin/pom.xml`:注释掉 `ruoyi-demo` 依赖
|
||||
- 各业务模块 pom(hq/rs/cw/xg/zsjy/jcbt/xskh/bzrkh/aqbw):同步注释 `ruoyi-demo` 传递依赖,避免再次打入 admin 包
|
||||
|
||||
> 源码目录 `ruoyi-modules/ruoyi-demo` 仍保留,便于本地开发需要时再启用;默认构建/部署不再加载其 Controller。
|
||||
|
||||
## 验证
|
||||
|
||||
重启后端后:
|
||||
|
||||
1. 访问 `/prod-api/swagger-ui.html`、`/prod-api/api-docs` → 不可用或 404/鉴权失败
|
||||
2. 访问 `/prod-api/demo/mail/sendSimpleMessage`、`/prod-api/demo/queue/delayed/subscribe` → 404(映射不存在)
|
||||
@@ -0,0 +1,19 @@
|
||||
# P0:`POST /rs/init/update-menu` 免登录整改说明
|
||||
|
||||
## 问题
|
||||
|
||||
`RsInitController#updateMenu` 标注了 `@SaIgnore`,未登录即可删除并重建 `sys_menu` / `sys_role_menu`(menu_id 2000–2028),属于未授权写操作。
|
||||
|
||||
## 整改
|
||||
|
||||
- 移除 `@SaIgnore`,恢复 Sa-Token 登录校验。
|
||||
- 增加 `@SaCheckPermission("system:menu:edit")`,仅具备系统菜单编辑权限的账号可调用。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsInitController.java`
|
||||
|
||||
## 验证
|
||||
|
||||
未带 Token 调用 `POST /prod-api/rs/init/update-menu` 应返回未登录(401)。
|
||||
已登录但无 `system:menu:edit` 权限应返回无权限。
|
||||
@@ -0,0 +1,15 @@
|
||||
# P1:CAS 换票密钥与日志脱敏整改说明
|
||||
|
||||
## 改动
|
||||
|
||||
1. **前端**:删除 `web/src/views/login.vue` 中未使用的 `CAS_CLIENT_SECRET`;换票仍只调 `GET /cas/accessToken?code=...`;控制台不再打印含 Token 的响应 body。
|
||||
2. **后端**:`CasController` 日志不再打印带 `client_secret` 的完整 URL、`access_token`、完整 profile;失败响应也不再回传 CAS 原始报文。
|
||||
|
||||
## 对登录的影响
|
||||
|
||||
无。前端本就未使用 secret;日志脱敏不改变换票与签发 Token 逻辑。
|
||||
|
||||
## 后续建议(未做)
|
||||
|
||||
- 将后端 `CLIENT_SECRET` 外置到配置/环境变量,并向 CAS 侧轮换密钥。
|
||||
- 对 `/cas/accessToken` 增加 IP 限流。
|
||||
@@ -0,0 +1,15 @@
|
||||
# P1:`POST /rs/application/submit` 须登录整改说明
|
||||
|
||||
## 问题
|
||||
|
||||
接口标注 `@SaIgnore`,且在 `security.excludes` 中放行,未登录即可任意提交应聘信息(姓名、手机号等写入库),存在灌数据/滥投风险。
|
||||
|
||||
## 整改
|
||||
|
||||
1. 移除 `RsApplicationController#submitApplication` 的 `@SaIgnore`
|
||||
2. 从 `application.yml` → `security.excludes` 删除 `/rs/application/submit`
|
||||
3. 提交时绑定 `applicantId = LoginHelper.getUserId()`
|
||||
|
||||
## 影响
|
||||
|
||||
招聘门户投递须先登录;未带 Token 调用返回未登录。职位公开浏览接口(`/rs/job/public/**`)不受影响。
|
||||
@@ -0,0 +1,30 @@
|
||||
-- ============================================================
|
||||
-- P4 整改:应用库账号(禁止业务使用 root)
|
||||
-- 说明:
|
||||
-- 1. 用现有 root 登录后执行本脚本(生产 172.16.0.67、测试库需各执行一遍)
|
||||
-- 2. 仅授权业务库 hzdyjs_xg.*,不含 *.* 全局 DBA 权限
|
||||
-- 3. 执行后修改应用配置并重启,再用下方「验证」确认
|
||||
-- ============================================================
|
||||
|
||||
-- 账号:hzdyjs_app
|
||||
-- 密码:Hzdyjs@App#n4SQi8AfPHldNj49
|
||||
|
||||
CREATE USER IF NOT EXISTS 'hzdyjs_app'@'%' IDENTIFIED BY 'Hzdyjs@App#n4SQi8AfPHldNj49';
|
||||
CREATE USER IF NOT EXISTS 'hzdyjs_app'@'localhost' IDENTIFIED BY 'Hzdyjs@App#n4SQi8AfPHldNj49';
|
||||
CREATE USER IF NOT EXISTS 'hzdyjs_app'@'172.16.0.%' IDENTIFIED BY 'Hzdyjs@App#n4SQi8AfPHldNj49';
|
||||
|
||||
-- 业务库完整权限(表级 ALL,仍不是全局 DBA)
|
||||
GRANT ALL PRIVILEGES ON `hzdyjs_xg`.* TO 'hzdyjs_app'@'%';
|
||||
GRANT ALL PRIVILEGES ON `hzdyjs_xg`.* TO 'hzdyjs_app'@'localhost';
|
||||
GRANT ALL PRIVILEGES ON `hzdyjs_xg`.* TO 'hzdyjs_app'@'172.16.0.%';
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
-- ---------- 验证(可选)----------
|
||||
-- SHOW GRANTS FOR 'hzdyjs_app'@'%';
|
||||
-- 期望类似:GRANT ALL PRIVILEGES ON `hzdyjs_xg`.* TO `hzdyjs_app`@`%`
|
||||
-- 不应出现:ON *.* 、SUPER、FILE、GRANT OPTION 等全局高危权限
|
||||
|
||||
-- 应用侧验证:
|
||||
-- SELECT USER(), CURRENT_USER();
|
||||
-- 期望:hzdyjs_app@...
|
||||
@@ -0,0 +1,13 @@
|
||||
-- P5 配套:关闭开放注册,缩小 /auth/register 免登攻击面
|
||||
-- 在业务库 hzdyjs_xg 执行
|
||||
|
||||
UPDATE sys_config
|
||||
SET config_value = 'false',
|
||||
update_time = NOW()
|
||||
WHERE config_key = 'sys.account.registerUser';
|
||||
|
||||
-- 验证
|
||||
SELECT config_key, config_value
|
||||
FROM sys_config
|
||||
WHERE config_key = 'sys.account.registerUser';
|
||||
-- 期望 config_value = false
|
||||
@@ -0,0 +1,13 @@
|
||||
-- P6:关闭开放注册(与 P5 配套,缩小免登 /auth/register 攻击面)
|
||||
-- 在业务库 hzdyjs_xg 执行
|
||||
|
||||
UPDATE sys_config
|
||||
SET config_value = 'false',
|
||||
update_time = NOW()
|
||||
WHERE config_key = 'sys.account.registerUser';
|
||||
|
||||
-- 验证
|
||||
SELECT config_id, config_key, config_value, remark
|
||||
FROM sys_config
|
||||
WHERE config_key = 'sys.account.registerUser';
|
||||
-- 期望 config_value = false
|
||||
@@ -0,0 +1,35 @@
|
||||
# P6 整改说明:注册接口免登可被滥用
|
||||
|
||||
## 问题
|
||||
|
||||
`AuthController` 类级 `@SaIgnore`,`POST /prod-api/auth/register` 无需登录即可访问。若数据库 `sys.account.registerUser=true`,可被滥用于撞库、刷注册,并曾作为 SQL 注入入口(见 P5)。
|
||||
|
||||
## 整改措施(已落地)
|
||||
|
||||
| # | 措施 | 位置 |
|
||||
|---|------|------|
|
||||
| 1 | 配置层总开关 `user.register.enabled`,**默认 false** | `application.yml` |
|
||||
| 2 | Controller 最先判断配置开关,关闭则直接拒绝 | `AuthController.register` |
|
||||
| 3 | Service 二次拦截,防止绕过 Controller | `SysRegisterService.register` |
|
||||
| 4 | IP 限流:60 秒内最多 5 次 | `@RateLimiter` |
|
||||
| 5 | 仍保留库开关 `sys.account.registerUser` 二次判断 | 原有逻辑 |
|
||||
| 6 | 配套 SQL 将库开关置为 false | `P6-关闭开放注册.sql` |
|
||||
|
||||
生效条件(同时满足才会真正注册成功):
|
||||
|
||||
1. `user.register.enabled=true`(配置,默认已关)
|
||||
2. `sys.account.registerUser=true`(数据库)
|
||||
|
||||
校园场景保持两者均为 false 即可。
|
||||
|
||||
## 发布注意
|
||||
|
||||
- 需重新编译发布 backend
|
||||
- 生产执行 `P6-关闭开放注册.sql`(若 P5 已执行过可跳过)
|
||||
- 验证:`POST /prod-api/auth/register` 应返回「当前系统没有开启注册功能」
|
||||
|
||||
## 与 P5 关系
|
||||
|
||||
- P5:修 `tenantId` SQL 注入(格式白名单)
|
||||
- P6:缩小免登注册攻击面(默认关闭 + 限流)
|
||||
- 两者互补,建议一并发布
|
||||
@@ -0,0 +1,29 @@
|
||||
-- ============================================================
|
||||
-- P7:重置业务管理员 js01 密码(BCrypt,与系统 Hutool BCrypt 一致)
|
||||
-- 新明文密码:Hzdyjs@Js01#K9mP2xQ7w
|
||||
-- 请在生产/测试库分别执行;执行后通知管理员用新密码登录并自行再改一次
|
||||
-- ============================================================
|
||||
|
||||
-- 执行前确认账号存在
|
||||
SELECT user_id, tenant_id, user_name, nick_name, status, del_flag,
|
||||
LEFT(password, 10) AS pwd_prefix
|
||||
FROM sys_user
|
||||
WHERE user_name = 'js01'
|
||||
AND del_flag = '0';
|
||||
|
||||
-- 重置密码
|
||||
UPDATE sys_user
|
||||
SET password = '$2a$10$8oUw5Tz8x6vQJlcQrcQZT.J/vjUAe2JjSNL/QdDT2XJqg.wX67csO',
|
||||
update_time = NOW()
|
||||
WHERE user_name = 'js01'
|
||||
AND del_flag = '0';
|
||||
|
||||
-- 验证(应影响 1 行;哈希前缀一般为 $2a$10$)
|
||||
SELECT user_id, user_name, nick_name, status,
|
||||
LEFT(password, 29) AS pwd_prefix,
|
||||
update_time
|
||||
FROM sys_user
|
||||
WHERE user_name = 'js01'
|
||||
AND del_flag = '0';
|
||||
|
||||
-- 可选:清理该用户在线会话需在 Redis/后台强制下线,SQL 无法直接踢 token
|
||||
@@ -1,22 +1,40 @@
|
||||
# SQL 注入漏洞整改说明(tenantId)
|
||||
# SQL 注入漏洞整改说明(tenantId / P5)
|
||||
|
||||
## 背景
|
||||
|
||||
攻防演练报告指出智慧校园(mobile H5:`ykt.hzdyjsxy.cn:18081/#/`)存在 SQL 注入,可拖取 `sys_user` / `sys_config` 等数据。
|
||||
攻防演练报告:免登接口 `POST /prod-api/auth/register` 的 `tenantId` 经多租户插件拼入 SQL,可 `extractvalue` 拖库(RuoYi-Vue-Plus XVE-2026-50962 同类问题)。
|
||||
|
||||
实际利用入口为后端免登接口 `POST /prod-api/auth/register` 的 `tenantId` 字段,经多租户插件拼入 SQL(与 RuoYi-Vue-Plus XVE-2026-50962 同类问题)。
|
||||
## 整改措施(代码已落地)
|
||||
|
||||
## 整改内容(2026-09-17)
|
||||
|
||||
| # | 措施 | 改动位置 |
|
||||
|---|------|----------|
|
||||
| 1 | sink 白名单:租户 ID 仅允许 `^[0-9]{1,20}$`,非法直接抛错,不拼 SQL | `PlusTenantLineHandler`、`TenantHelper.checkTenantIdFormat` |
|
||||
| # | 措施 | 位置 |
|
||||
|---|------|------|
|
||||
| 1 | sink 白名单:租户 ID 仅 `^[0-9]{1,20}$`,非法不拼 SQL | `PlusTenantLineHandler`、`TenantHelper.checkTenantIdFormat` |
|
||||
| 2 | 注册接口先 `checkTenant`,再查注册开关 | `AuthController.register` |
|
||||
| 3 | 所有 `TenantHelper.dynamic/setDynamic` 入口统一格式校验;登录 `checkTenant` 增加格式校验 | `TenantHelper`、`SysLoginService.checkTenant` |
|
||||
| 4 | 删除心理咨询调试任意 SQL 接口 | 删除 `XljkConsultationDebugController` |
|
||||
| 3 | 登录/动态租户入口统一格式校验 | `SysLoginService.checkTenant`、`TenantHelper.setDynamic` |
|
||||
| 4 | 入参 Bean 校验 `@Pattern` | `LoginBody.tenantId`(含注册) |
|
||||
| 5 | `selectRegisterEnabled` 入口再校验 | `SysConfigServiceImpl` |
|
||||
| 6 | 社交登录绑定参数校验 | `AuthController.authBinding` |
|
||||
| 7 | 删除心理咨询调试任意 SQL 接口 | 已删除 `XljkConsultationDebugController` |
|
||||
|
||||
## 发布注意
|
||||
## 配套 SQL(生产建议执行)
|
||||
|
||||
- 需重新编译并发布 **backend** 后生效;仅发 mobile 前端无效。
|
||||
- 建议同步轮换初始密码、微信 AppSecret,并审计异常访问。
|
||||
- 生产环境确认 `sys.account.registerUser=false`(关闭开放注册)。
|
||||
关闭开放注册,缩小免登攻击面:
|
||||
|
||||
```sql
|
||||
UPDATE sys_config
|
||||
SET config_value = 'false',
|
||||
update_time = NOW()
|
||||
WHERE config_key = 'sys.account.registerUser'
|
||||
AND deleted = 0;
|
||||
```
|
||||
|
||||
## 发布与验证
|
||||
|
||||
1. 重新编译发布 **backend**(仅发前端无效)
|
||||
2. 用报告同类 payload 打 `/prod-api/auth/register`,应返回租户编号格式不正确 / 未开启注册,**不应**再出现 XPATH / SQL 报错泄密
|
||||
3. 正常 `tenantId=000000` 登录不受影响
|
||||
|
||||
## 状态
|
||||
|
||||
- 代码:已完成
|
||||
- 生产生效:取决于是否已用含本修复的包重启
|
||||
@@ -0,0 +1 @@
|
||||
{"code":404,"msg":"No endpoint GET /actuator/metrics.","data":null}
|
||||
@@ -0,0 +1,9 @@
|
||||
HTTP/1.1 200 OK
|
||||
Connection: keep-alive
|
||||
Vary: Origin
|
||||
Vary: Access-Control-Request-Method
|
||||
Vary: Access-Control-Request-Headers
|
||||
Transfer-Encoding: chunked
|
||||
Content-Type: application/json
|
||||
Date: Sun, 20 Sep 2026 03:34:05 GMT
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"code":404,"msg":"No endpoint GET /actuator/heapdump.","data":null}
|
||||
@@ -0,0 +1 @@
|
||||
{"clientId": "e5cd7e4891bf95d1d19206ce24a7b32e", "grantType": "password", "tenantId": "000000' AND extractvalue(1,concat(0x7e,(SELECT version()))) AND '1'='1", "username": "zzq001", "password": "ttt", "userType": "sys_user", "code": "", "uuid": ""}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":404,"msg":"No endpoint GET /actuator/env.","data":null}
|
||||
@@ -0,0 +1 @@
|
||||
{"code": 401, "msg": "no basic auth", "data": null}
|
||||
@@ -0,0 +1,2 @@
|
||||
{"code":500,"msg":"租户编号格式不正确, 用户密码长度必须在5到30个字符之间","data":null}
|
||||
HTTP:200
|
||||
@@ -0,0 +1,2 @@
|
||||
{"code":500,"msg":"当前系统没有开启注册功能!","data":null}
|
||||
HTTP:200
|
||||
@@ -0,0 +1 @@
|
||||
{"clientId": "e5cd7e4891bf95d1d19206ce24a7b32e", "grantType": "password", "tenantId": "000000", "username": "ptest_p6_1789875176", "password": "Test@123456", "userType": "sys_user", "code": "", "uuid": ""}
|
||||
@@ -0,0 +1,2 @@
|
||||
{"code":200,"msg":"操作成功","data":{"tenantEnabled":true,"voList":[{"tenantId":"000000","companyName":"杭州第一技师学院","domain":null}]}}
|
||||
HTTP:200
|
||||
@@ -0,0 +1,22 @@
|
||||
# 本地测试环境 P1–P9 验证记录
|
||||
|
||||
- 验证时间:2026-09-20
|
||||
- 环境:本机 backend `:8080` + web `:80`(`/prod-api` 代理)
|
||||
- 测试库:`47.99.164.66 / hzdyjs_xg`
|
||||
|
||||
| 编号 | 结论 | 验证证据 |
|
||||
|------|------|----------|
|
||||
| P1 | 通过 | `ruoyi:123456` → 401;`ruoyi:HzdyjsMon@Dev#9Kx7mQ2pL` → Basic 鉴权通过 |
|
||||
| P2 | 通过 | `/actuator/heapdump` 返回 `No endpoint GET /actuator/heapdump`,无堆转储文件 |
|
||||
| P3 | 通过(攻击链已断) | 依赖 P1/P2;无法再远程 dump 内存凭据 |
|
||||
| P4 | 通过 | 配置 `username: hzdyjs_app`;库账号可连;`CURRENT_USER=hzdyjs_app@%` |
|
||||
| P5 | 通过 | 注入 `tenantId` → `租户编号格式不正确`,无 XPATH/SQL 泄密 |
|
||||
| P6 | 通过 | 正常注册 → `当前系统没有开启注册功能!`;库 `registerUser=false` |
|
||||
| P7 | 本地 N/A / 生产已改 | 测试库无 `js01`;生产已执行 P7 脚本并确认哈希更新 |
|
||||
| P8 | 通过(入口已堵) | heapdump/注入入口不可再利用;历史泄露数据需事件侧收尾 |
|
||||
| P9 | 通过 | 敏感端点均不可用;仅残留空的 `/actuator` 发现页(无子端点) |
|
||||
|
||||
## 补充说明
|
||||
|
||||
- `/actuator` 根路径在正确监控密码下仍可能返回空 `_links`(仅 self),**不含 health/heapdump/env**,不影响安全结论。
|
||||
- 前端 `http://127.0.0.1/` 与 `GET /auth/tenant/list` 正常(HTTP 200)。
|
||||
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user