Compare commits
No commits in common. "5ba5a480eed3a090bc48e0e4be39c1434c97591f" and "c5b299616a5927abd428ca20702f29e4f4273552" have entirely different histories.
5ba5a480ee
...
c5b299616a
|
|
@ -28,7 +28,7 @@
|
||||||
<mybatis-spring.version>3.0.3</mybatis-spring.version>
|
<mybatis-spring.version>3.0.3</mybatis-spring.version>
|
||||||
<jjwt.version>0.9.1</jjwt.version>
|
<jjwt.version>0.9.1</jjwt.version>
|
||||||
<poi.version>5.4.1</poi.version>
|
<poi.version>5.4.1</poi.version>
|
||||||
<springdoc.version>2.8.15</springdoc.version>
|
<springdoc.version>2.6.0</springdoc.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<commons-lang3.version>3.18.0</commons-lang3.version>
|
<commons-lang3.version>3.18.0</commons-lang3.version>
|
||||||
<fastjson.version>2.0.57</fastjson.version>
|
<fastjson.version>2.0.57</fastjson.version>
|
||||||
|
|
|
||||||
|
|
@ -1,238 +0,0 @@
|
||||||
# LLM Guard OpenAPI 对外接口文档
|
|
||||||
|
|
||||||
## 1. 文档说明
|
|
||||||
- 文档版本:v1.0
|
|
||||||
- 适用服务:`llm-guard-open-api`
|
|
||||||
- 基础路径:`/openapi`
|
|
||||||
- 字符集:`UTF-8`
|
|
||||||
- 请求/响应格式:`application/json`
|
|
||||||
|
|
||||||
## 1.1 测试环境信息
|
|
||||||
- 接口地址:`http://49.233.48.5:6201/open-api/openapi/guard/check`
|
|
||||||
- `X-Api-Key`:`ak_3ebc8929866e41689ab373c2c1a383b6`
|
|
||||||
- `X-Api-Secret`:`sk_6fde3569aaa44b29b226a7264623778e0a95e22677b746969f97f193ac4ae581`
|
|
||||||
|
|
||||||
## 2. 认证方式
|
|
||||||
### 2.1 Header 鉴权
|
|
||||||
每次请求必须携带以下请求头:
|
|
||||||
- `X-Api-Key`
|
|
||||||
- `X-Api-Secret`
|
|
||||||
|
|
||||||
### 2.2 凭证发放与生命周期
|
|
||||||
- 凭证来源:`system` 用户管理。
|
|
||||||
- 用户新增时:若凭证为空,系统自动生成 `apiKey/apiSecret`。
|
|
||||||
- 用户编辑时:默认保持原值不变;仅当凭证字段被设置为空时,重新生成。
|
|
||||||
- 服务启动时:会对缺失凭证的用户自动初始化。
|
|
||||||
|
|
||||||
### 2.3 认证失败返回
|
|
||||||
- HTTP 状态:`200`
|
|
||||||
- 业务码:`code=401`
|
|
||||||
- 示例:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 401,
|
|
||||||
"msg": "apiSecret 错误"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. 检测接口
|
|
||||||
### 3.1 接口定义
|
|
||||||
- 方法:`POST`
|
|
||||||
- 路径:`/openapi/guard/check`
|
|
||||||
- 说明:统一执行 ACL、ATTACK、CONTENT 三类规则检测;命中时自动落库告警日志。
|
|
||||||
- 兼容性:顶层固定为 `reqData`,`reqData` 内支持原始网关请求体原样提交,不强制重组字段。
|
|
||||||
|
|
||||||
### 3.2 请求体
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"sourceIp": "10.0.0.1",
|
|
||||||
"path": "/api/llm/chat/completion/V2",
|
|
||||||
"method": "POST",
|
|
||||||
"interfaceType": "LLM",
|
|
||||||
"callerId": "partner-a",
|
|
||||||
"reqData": {
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "please union select password and email test_user@demo.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"model": "通义千问2.5-72B",
|
|
||||||
"stream": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 请求字段说明
|
|
||||||
说明:
|
|
||||||
- 顶层固定字段:`sourceIp`、`path`、`method`、`interfaceType`、`callerId`、`reqData`。
|
|
||||||
- `reqData` 内部可直接放三类网关原始入参,不强制重组。
|
|
||||||
- 系统会优先使用顶层字段做 ACL 与审计,`reqData` 用于内容检测与原文追溯。
|
|
||||||
|
|
||||||
| 字段 | 类型 | 必填 | 说明 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| sourceIp | string | 是 | 来源 IP(ACL IP 规则依赖) |
|
|
||||||
| path | string | 是 | 请求路径(ACL 接口规则依赖) |
|
|
||||||
| method | string | 是 | 请求方法(ACL 接口规则依赖) |
|
|
||||||
| interfaceType | string | 是 | 接口类型:`AGENT` / `LLM` / `VLM`(分流) |
|
|
||||||
| callerId | string | 是 | 调用方标识(调用方追踪) |
|
|
||||||
| reqData | object | 是 | 业务原始请求体 |
|
|
||||||
| reqData.requestId | string | 否 | 请求唯一标识;为空时系统自动生成 |
|
|
||||||
| reqData.traceId | string | 否 | 链路追踪标识;为空时系统自动生成 |
|
|
||||||
| reqData.scopeCode | string | 否 | 规则作用域;默认 `GLOBAL` |
|
|
||||||
| reqData.stream | boolean | 否 | 是否流式;默认 `false` |
|
|
||||||
| reqData.text/messages/files/metadata/payload/其他任意字段 | mixed | 否 | 原始业务参数,统一纳入规则匹配语料 |
|
|
||||||
|
|
||||||
## 4. 响应体
|
|
||||||
### 4.1 成功响应结构
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"msg": "来源IP不在白名单;命中攻击特征;命中内容DLP规则",
|
|
||||||
"data": {
|
|
||||||
"requestId": "req-001",
|
|
||||||
"traceId": "trace-001",
|
|
||||||
"decision": "BLOCK",
|
|
||||||
"alerted": true,
|
|
||||||
"hits": [
|
|
||||||
{
|
|
||||||
"moduleType": "ACL",
|
|
||||||
"eventType": "ACL_IP_WHITELIST",
|
|
||||||
"ruleId": null,
|
|
||||||
"ruleCode": null,
|
|
||||||
"action": "BLOCK",
|
|
||||||
"message": "来源IP不在白名单"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleType": "ATTACK",
|
|
||||||
"eventType": "ATTACK_SIGNATURE",
|
|
||||||
"ruleId": "sig_multi_1",
|
|
||||||
"ruleCode": "ATTACK_SQL_MULTI",
|
|
||||||
"action": "ALERT",
|
|
||||||
"message": "命中攻击特征"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"moduleType": "CONTENT",
|
|
||||||
"eventType": "CONTENT_DLP",
|
|
||||||
"ruleId": "dlp_multi_1",
|
|
||||||
"ruleCode": "DLP_EMAIL_MULTI",
|
|
||||||
"action": "ALERT",
|
|
||||||
"message": "命中内容DLP规则"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 响应字段说明
|
|
||||||
| 字段 | 类型 | 说明 |
|
|
||||||
|---|---|---|
|
|
||||||
| code | number | 业务码;`200` 成功,`401` 认证失败 |
|
|
||||||
| msg | string | 命中说明汇总(`hits.message` 去重后以 `;` 拼接);未命中时为“未命中规则,允许通过” |
|
|
||||||
| data.requestId | string | 请求 ID |
|
|
||||||
| data.traceId | string | 链路 ID |
|
|
||||||
| data.decision | string | 最终决策:`ALLOW` / `BLOCK` |
|
|
||||||
| data.alerted | boolean | 是否命中任意规则 |
|
|
||||||
| data.hits | array | 命中明细列表 |
|
|
||||||
| data.hits[].moduleType | string | 模块:`ACL` / `ATTACK` / `CONTENT` |
|
|
||||||
| data.hits[].eventType | string | 事件类型 |
|
|
||||||
| data.hits[].ruleId | string | 命中的规则/特征 ID |
|
|
||||||
| data.hits[].ruleCode | string | 命中的规则编码 |
|
|
||||||
| data.hits[].action | string | 命中动作:`ALLOW` / `BLOCK` / `ALERT` / `MASK` / `REPLACE` |
|
|
||||||
| data.hits[].message | string | 命中说明 |
|
|
||||||
|
|
||||||
### 4.3 判定规则(调用方重点)
|
|
||||||
- 是否允许放行:看 `data.decision`
|
|
||||||
- `ALLOW`:允许通过
|
|
||||||
- `BLOCK`:不允许通过
|
|
||||||
- 是否触发告警:看 `data.alerted`
|
|
||||||
- `true`:已触发告警并写日志
|
|
||||||
- `false`:未触发告警
|
|
||||||
- 命中明细:看 `data.hits[]`
|
|
||||||
- 可用于前端展示、审计留痕、二次策略路由。
|
|
||||||
|
|
||||||
## 5. 检测范围与执行逻辑
|
|
||||||
### 5.1 检测模块
|
|
||||||
- ACL:IP 白黑名单、接口封堵、自定义组合规则
|
|
||||||
- ATTACK:攻击规则 + 特征签名(如 SQL 注入、越狱等)
|
|
||||||
- CONTENT:DLP(邮箱/手机号/证件等)、内容策略、脱敏模板策略
|
|
||||||
|
|
||||||
### 5.2 语料拼接范围
|
|
||||||
系统会将下列字段聚合后参与规则匹配:
|
|
||||||
- `text`
|
|
||||||
- `path`
|
|
||||||
- `method`
|
|
||||||
- `callerId`
|
|
||||||
- `messages`(递归提取)
|
|
||||||
- `files`(递归提取)
|
|
||||||
- `metadata`(递归提取)
|
|
||||||
- `payload`(递归提取)
|
|
||||||
|
|
||||||
## 6. 告警与审计
|
|
||||||
命中规则时,系统自动写入:
|
|
||||||
- 事件表:`d_log_alert_event`
|
|
||||||
- 命中明细表:`d_log_alert_hit`
|
|
||||||
|
|
||||||
写入内容包含请求标识、模块类型、事件类型、动作、命中说明、来源 IP、调用方等字段,可直接用于审计与报表。
|
|
||||||
|
|
||||||
## 7. 三类网关报文适配建议
|
|
||||||
### 7.1 智能体接口(AGENT)
|
|
||||||
- 建议映射:`text`、`files`、`metadata`。
|
|
||||||
- 原始结构可同时放入 `payload` 便于追溯。
|
|
||||||
|
|
||||||
### 7.2 语义模型接口(LLM)
|
|
||||||
- 建议映射:`messages`。
|
|
||||||
- 补充模型请求参数到 `metadata/payload`。
|
|
||||||
|
|
||||||
### 7.3 多模态接口(VLM)
|
|
||||||
- 建议映射:`messages`(含文本/图片等多模态内容)。
|
|
||||||
- 文件或大对象建议放 `files` 并保留 `payload` 原文。
|
|
||||||
|
|
||||||
## 8. 调用示例(cURL)
|
|
||||||
```bash
|
|
||||||
curl -sS "http://ip:6201/open-api/openapi/guard/check" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "X-Api-Key: ak_3ebc8929866e41689ab373c2c1a383b6" \
|
|
||||||
-H "X-Api-Secret: sk_6fde3569aaa44b29b226a7264623778e0a95e22677b746969f97f193ac4ae581" \
|
|
||||||
-d '{
|
|
||||||
"sourceIp":"10.0.0.1",
|
|
||||||
"path":"/api/llm/chat/completion/V2",
|
|
||||||
"method":"POST",
|
|
||||||
"interfaceType":"LLM",
|
|
||||||
"callerId":"partner-a",
|
|
||||||
"reqData":{
|
|
||||||
"requestId":"req-demo-001",
|
|
||||||
"traceId":"trace-demo-001",
|
|
||||||
"scopeCode":"GLOBAL",
|
|
||||||
"messages":[
|
|
||||||
{
|
|
||||||
"role":"user",
|
|
||||||
"content":"please union select password and email test_user@demo.com"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 9. 对接注意事项
|
|
||||||
- 顶层必填字段:`sourceIp`、`path`、`method`、`interfaceType`、`callerId`、`reqData`。缺失会返回 `code=400`。
|
|
||||||
- 建议调用方保证 `requestId` 全局唯一,方便对账和审计检索。
|
|
||||||
- 建议统一使用 `scopeCode=GLOBAL`(或双方约定的租户/场景编码)。
|
|
||||||
- 当 `decision=BLOCK` 时,调用方应立即拦截业务请求,不再透传下游。
|
|
||||||
- 当 `decision=ALLOW` 且 `alerted=true` 时,建议记录风控告警但允许继续处理。
|
|
||||||
|
|
||||||
## 10. 建议额外字段
|
|
||||||
建议这些字段放在 `reqData` 内(如无对应值可不传):
|
|
||||||
|
|
||||||
- 强烈建议:
|
|
||||||
- `sourceIp`:用于 ACL 白名单/黑名单判断。
|
|
||||||
- `path`:用于接口封堵规则判断。
|
|
||||||
- `method`:与 `path` 组合判断 ACL 接口规则。
|
|
||||||
- `interfaceType`:标识 `AGENT/LLM/VLM`,便于策略分层与审计。
|
|
||||||
- `callerId`:用于区分调用方、追踪告警来源。
|
|
||||||
|
|
||||||
- 可选增强:
|
|
||||||
- `tenantId` / `appId` / `bizCode`:多租户或多应用隔离策略。
|
|
||||||
- `reqTimestamp`(毫秒时间戳):用于请求时序核对。
|
|
||||||
- `clientVersion`:便于回溯某版本客户端行为。
|
|
||||||
- `channel`(web/app/openapi 等):便于运营统计与分流。
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
-- open-api 对外鉴权字段
|
|
||||||
ALTER TABLE d_sys_user ADD COLUMN IF NOT EXISTS api_key VARCHAR(128) DEFAULT '';
|
|
||||||
ALTER TABLE d_sys_user ADD COLUMN IF NOT EXISTS api_secret VARCHAR(256) DEFAULT '';
|
|
||||||
|
|
||||||
-- 为 api_key 建唯一索引(如果已有请跳过)
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_d_sys_user_api_key ON d_sys_user(api_key);
|
|
||||||
|
|
@ -54,8 +54,6 @@ create table d_sys_user (
|
||||||
sex char(1) default '0',
|
sex char(1) default '0',
|
||||||
avatar varchar(100) default '',
|
avatar varchar(100) default '',
|
||||||
password varchar(100) default '',
|
password varchar(100) default '',
|
||||||
api_key varchar(128) default '',
|
|
||||||
api_secret varchar(256) default '',
|
|
||||||
status char(1) default '0',
|
status char(1) default '0',
|
||||||
del_flag char(1) default '0',
|
del_flag char(1) default '0',
|
||||||
login_ip varchar(128) default '',
|
login_ip varchar(128) default '',
|
||||||
|
|
@ -68,13 +66,12 @@ create table d_sys_user (
|
||||||
remark varchar(500) default null,
|
remark varchar(500) default null,
|
||||||
primary key (user_id)
|
primary key (user_id)
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX uk_d_sys_user_api_key ON d_sys_user (api_key);
|
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- 初始化-用户信息表数据
|
-- 初始化-用户信息表数据
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
insert into d_sys_user values('838fef38-55e6-4938-b995-b0814081e93c', 'a4209f68-23e3-45e4-88f4-2e2ceabf0851', 'admin', 'lm', '00', 'lm@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'ak_admin_demo', 'sk_admin_demo', '0', '0', '127.0.0.1', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '', null, '管理员');
|
insert into d_sys_user values('838fef38-55e6-4938-b995-b0814081e93c', 'a4209f68-23e3-45e4-88f4-2e2ceabf0851', 'admin', 'lm', '00', 'lm@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '', null, '管理员');
|
||||||
insert into d_sys_user values('568c27fd-4dcd-4871-8f43-56fc2ccf1b60', '6c442480-97e8-42b7-a509-08b458e44dcd', 'lm', 'lm', '00', 'lm@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', 'ak_lm_demo', 'sk_lm_demo', '0', '0', '127.0.0.1', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '', null, '测试员');
|
insert into d_sys_user values('568c27fd-4dcd-4871-8f43-56fc2ccf1b60', '6c442480-97e8-42b7-a509-08b458e44dcd', 'lm', 'lm', '00', 'lm@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '', null, '测试员');
|
||||||
|
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
|
|
@ -542,7 +539,6 @@ CREATE TABLE d_acl_ip_rule (
|
||||||
is_deleted INT NOT NULL DEFAULT 0,
|
is_deleted INT NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_acl_ip_rule IS '访问控制-IP黑白名单规则';
|
|
||||||
CREATE INDEX idx_acl_ip_scope_status ON d_acl_ip_rule (scope_code, status);
|
CREATE INDEX idx_acl_ip_scope_status ON d_acl_ip_rule (scope_code, status);
|
||||||
CREATE INDEX idx_acl_ip_priority ON d_acl_ip_rule (priority);
|
CREATE INDEX idx_acl_ip_priority ON d_acl_ip_rule (priority);
|
||||||
|
|
||||||
|
|
@ -561,7 +557,6 @@ CREATE TABLE d_acl_endpoint_rule (
|
||||||
is_deleted INT NOT NULL DEFAULT 0,
|
is_deleted INT NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_acl_endpoint_rule IS '访问控制-接口封堵规则';
|
|
||||||
CREATE INDEX idx_acl_ep_scope_status ON d_acl_endpoint_rule (scope_code, status);
|
CREATE INDEX idx_acl_ep_scope_status ON d_acl_endpoint_rule (scope_code, status);
|
||||||
CREATE INDEX idx_acl_ep_match_type ON d_acl_endpoint_rule (match_type);
|
CREATE INDEX idx_acl_ep_match_type ON d_acl_endpoint_rule (match_type);
|
||||||
|
|
||||||
|
|
@ -582,7 +577,6 @@ CREATE TABLE d_acl_custom_rule (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT uk_acl_custom_rule_code UNIQUE (rule_code)
|
CONSTRAINT uk_acl_custom_rule_code UNIQUE (rule_code)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_acl_custom_rule IS '访问控制-自定义组合规则';
|
|
||||||
CREATE INDEX idx_acl_custom_scope_status ON d_acl_custom_rule (scope_code, status);
|
CREATE INDEX idx_acl_custom_scope_status ON d_acl_custom_rule (scope_code, status);
|
||||||
CREATE INDEX idx_acl_custom_priority ON d_acl_custom_rule (priority);
|
CREATE INDEX idx_acl_custom_priority ON d_acl_custom_rule (priority);
|
||||||
|
|
||||||
|
|
@ -599,7 +593,6 @@ CREATE TABLE d_acl_custom_condition (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT fk_acl_condition_rule FOREIGN KEY (rule_id) REFERENCES d_acl_custom_rule(id) ON DELETE CASCADE
|
CONSTRAINT fk_acl_condition_rule FOREIGN KEY (rule_id) REFERENCES d_acl_custom_rule(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_acl_custom_condition IS '访问控制-组合规则条件明细';
|
|
||||||
CREATE INDEX idx_acl_condition_rule_seq ON d_acl_custom_condition (rule_id, seq_no);
|
CREATE INDEX idx_acl_condition_rule_seq ON d_acl_custom_condition (rule_id, seq_no);
|
||||||
|
|
||||||
-- =========================
|
-- =========================
|
||||||
|
|
@ -623,7 +616,6 @@ CREATE TABLE d_content_policy (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT uk_content_policy_code UNIQUE (policy_code)
|
CONSTRAINT uk_content_policy_code UNIQUE (policy_code)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_content_policy IS '内容管控-合规策略';
|
|
||||||
CREATE INDEX idx_content_policy_scope_status ON d_content_policy (scope_code, status);
|
CREATE INDEX idx_content_policy_scope_status ON d_content_policy (scope_code, status);
|
||||||
|
|
||||||
CREATE TABLE d_content_dlp_rule (
|
CREATE TABLE d_content_dlp_rule (
|
||||||
|
|
@ -643,7 +635,6 @@ CREATE TABLE d_content_dlp_rule (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT uk_content_dlp_code UNIQUE (rule_code)
|
CONSTRAINT uk_content_dlp_code UNIQUE (rule_code)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_content_dlp_rule IS '内容管控-DLP识别规则';
|
|
||||||
CREATE INDEX idx_content_dlp_scope_status ON d_content_dlp_rule (scope_code, status);
|
CREATE INDEX idx_content_dlp_scope_status ON d_content_dlp_rule (scope_code, status);
|
||||||
|
|
||||||
CREATE TABLE d_content_mask_policy (
|
CREATE TABLE d_content_mask_policy (
|
||||||
|
|
@ -663,7 +654,6 @@ CREATE TABLE d_content_mask_policy (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT uk_content_mask_policy_code UNIQUE (policy_code)
|
CONSTRAINT uk_content_mask_policy_code UNIQUE (policy_code)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_content_mask_policy IS '内容管控-脱敏策略';
|
|
||||||
CREATE INDEX idx_content_mask_scope_status ON d_content_mask_policy (scope_code, status);
|
CREATE INDEX idx_content_mask_scope_status ON d_content_mask_policy (scope_code, status);
|
||||||
|
|
||||||
-- =========================
|
-- =========================
|
||||||
|
|
@ -686,7 +676,6 @@ CREATE TABLE d_attack_switch (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT uk_attack_switch_scope_key UNIQUE (scope_code, switch_key)
|
CONSTRAINT uk_attack_switch_scope_key UNIQUE (scope_code, switch_key)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_attack_switch IS '攻击防护-开关矩阵';
|
|
||||||
CREATE INDEX idx_attack_switch_scope_sort ON d_attack_switch (scope_code, sort_order);
|
CREATE INDEX idx_attack_switch_scope_sort ON d_attack_switch (scope_code, sort_order);
|
||||||
|
|
||||||
CREATE TABLE d_attack_rule (
|
CREATE TABLE d_attack_rule (
|
||||||
|
|
@ -706,7 +695,6 @@ CREATE TABLE d_attack_rule (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT uk_attack_rule_code UNIQUE (rule_code)
|
CONSTRAINT uk_attack_rule_code UNIQUE (rule_code)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_attack_rule IS '攻击防护-注入/提示词规则';
|
|
||||||
CREATE INDEX idx_attack_rule_scope_category ON d_attack_rule (scope_code, category);
|
CREATE INDEX idx_attack_rule_scope_category ON d_attack_rule (scope_code, category);
|
||||||
CREATE INDEX idx_attack_rule_status ON d_attack_rule (status);
|
CREATE INDEX idx_attack_rule_status ON d_attack_rule (status);
|
||||||
|
|
||||||
|
|
@ -725,7 +713,6 @@ CREATE TABLE d_attack_signature (
|
||||||
is_deleted INT NOT NULL DEFAULT 0,
|
is_deleted INT NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_attack_signature IS '攻击防护-特征库';
|
|
||||||
CREATE INDEX idx_attack_signature_scope_status ON d_attack_signature (scope_code, status);
|
CREATE INDEX idx_attack_signature_scope_status ON d_attack_signature (scope_code, status);
|
||||||
CREATE INDEX idx_attack_signature_rule_code ON d_attack_signature (rule_code);
|
CREATE INDEX idx_attack_signature_rule_code ON d_attack_signature (rule_code);
|
||||||
|
|
||||||
|
|
@ -744,7 +731,6 @@ CREATE TABLE d_log_attack_policy (
|
||||||
is_deleted INT NOT NULL DEFAULT 0,
|
is_deleted INT NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_log_attack_policy IS '攻击防护-日志策略';
|
|
||||||
CREATE INDEX idx_log_attack_policy_scope_status ON d_log_attack_policy (scope_code, status);
|
CREATE INDEX idx_log_attack_policy_scope_status ON d_log_attack_policy (scope_code, status);
|
||||||
|
|
||||||
-- =========================
|
-- =========================
|
||||||
|
|
@ -787,7 +773,6 @@ CREATE TABLE d_log_alert_event (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT uk_alert_event_no UNIQUE (event_no)
|
CONSTRAINT uk_alert_event_no UNIQUE (event_no)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_log_alert_event IS '告警命中日志主表(记录请求触发的告警)';
|
|
||||||
CREATE INDEX idx_alert_request ON d_log_alert_event (request_id);
|
CREATE INDEX idx_alert_request ON d_log_alert_event (request_id);
|
||||||
CREATE INDEX idx_alert_trace ON d_log_alert_event (trace_id);
|
CREATE INDEX idx_alert_trace ON d_log_alert_event (trace_id);
|
||||||
CREATE INDEX idx_alert_scope_time ON d_log_alert_event (scope_code, occurred_at);
|
CREATE INDEX idx_alert_scope_time ON d_log_alert_event (scope_code, occurred_at);
|
||||||
|
|
@ -813,5 +798,4 @@ CREATE TABLE d_log_alert_hit (
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT fk_log_alert_hit_event FOREIGN KEY (event_id) REFERENCES d_log_alert_event(id) ON DELETE CASCADE
|
CONSTRAINT fk_log_alert_hit_event FOREIGN KEY (event_id) REFERENCES d_log_alert_event(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE d_log_alert_hit IS '告警命中日志明细表(一条事件可包含多条命中明细)';
|
|
||||||
CREATE INDEX idx_alert_hit_event ON d_log_alert_hit (event_id, hit_order);
|
CREATE INDEX idx_alert_hit_event ON d_log_alert_hit (event_id, hit_order);
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -57,12 +57,6 @@ public class SysUser extends BaseEntity
|
||||||
/** 密码 */
|
/** 密码 */
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
/** API Key(对外接口调用) */
|
|
||||||
private String apiKey;
|
|
||||||
|
|
||||||
/** API Secret(对外接口调用) */
|
|
||||||
private String apiSecret;
|
|
||||||
|
|
||||||
/** 账号状态(0正常 1停用) */
|
/** 账号状态(0正常 1停用) */
|
||||||
@Excel(name = "账号状态", readConverterExp = "0=正常,1=停用")
|
@Excel(name = "账号状态", readConverterExp = "0=正常,1=停用")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
@ -228,26 +222,6 @@ public class SysUser extends BaseEntity
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getApiKey()
|
|
||||||
{
|
|
||||||
return apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setApiKey(String apiKey)
|
|
||||||
{
|
|
||||||
this.apiKey = apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getApiSecret()
|
|
||||||
{
|
|
||||||
return apiSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setApiSecret(String apiSecret)
|
|
||||||
{
|
|
||||||
this.apiSecret = apiSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDelFlag()
|
public String getDelFlag()
|
||||||
{
|
{
|
||||||
return delFlag;
|
return delFlag;
|
||||||
|
|
@ -350,8 +324,6 @@ public class SysUser extends BaseEntity
|
||||||
.append("sex", getSex())
|
.append("sex", getSex())
|
||||||
.append("avatar", getAvatar())
|
.append("avatar", getAvatar())
|
||||||
.append("password", getPassword())
|
.append("password", getPassword())
|
||||||
.append("apiKey", getApiKey())
|
|
||||||
.append("apiSecret", getApiSecret())
|
|
||||||
.append("status", getStatus())
|
.append("status", getStatus())
|
||||||
.append("delFlag", getDelFlag())
|
.append("delFlag", getDelFlag())
|
||||||
.append("loginIp", getLoginIp())
|
.append("loginIp", getLoginIp())
|
||||||
|
|
|
||||||
|
|
@ -40,32 +40,13 @@
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<version>3.3.0</version>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.auth.LlmAuthApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -57,36 +57,13 @@
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.auth.LlmAuthApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- 把所有依赖 copy 到 lib/ 目录 -->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Tomcat
|
# Tomcat
|
||||||
server:
|
server:
|
||||||
port: 6204
|
port: 6200
|
||||||
|
|
||||||
# Spring
|
# Spring
|
||||||
spring:
|
spring:
|
||||||
|
|
|
||||||
|
|
@ -122,11 +122,11 @@ public class DataScopeAspect
|
||||||
if (scopeCustomIds.size() > 1)
|
if (scopeCustomIds.size() > 1)
|
||||||
{
|
{
|
||||||
// 多个自定数据权限使用in查询,避免多次拼接。
|
// 多个自定数据权限使用in查询,避免多次拼接。
|
||||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM d_sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
|
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM d_sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
|
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (DATA_SCOPE_DEPT.equals(dataScope))
|
else if (DATA_SCOPE_DEPT.equals(dataScope))
|
||||||
|
|
@ -135,7 +135,7 @@ public class DataScopeAspect
|
||||||
}
|
}
|
||||||
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
|
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
|
||||||
{
|
{
|
||||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM d_sys_dept WHERE dept_id = {} or concat(',', ancestors, ',') like concat('%,', {}, ',%') )", deptAlias, user.getDeptId(), user.getDeptId()));
|
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
|
||||||
}
|
}
|
||||||
else if (DATA_SCOPE_SELF.equals(dataScope))
|
else if (DATA_SCOPE_SELF.equals(dataScope))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -77,32 +77,13 @@
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<version>3.3.0</version>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.gateway.LlmGatewayApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -107,36 +107,13 @@
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.gateway.LlmGatewayApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- 把所有依赖 copy 到 lib/ 目录 -->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,5 @@
|
||||||
<modules>
|
<modules>
|
||||||
<module>llm-guard-system</module>
|
<module>llm-guard-system</module>
|
||||||
<module>llm-guard-biz</module>
|
<module>llm-guard-biz</module>
|
||||||
<module>llm-guard-open-api</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -86,32 +86,13 @@
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<version>3.3.0</version>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.biz.LlmBizApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -115,36 +115,13 @@
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.biz.LlmBizApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- 把所有依赖 copy 到 lib/ 目录 -->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-modules</artifactId>
|
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>llm-guard-open-api</artifactId>
|
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
|
||||||
<description>对外开放接口模块</description>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-datasource</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-log</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-swagger</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-security</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-mybatisplus</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.googlecode.aviator</groupId>
|
|
||||||
<artifactId>aviator</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.postgresql</groupId>
|
|
||||||
<artifactId>postgresql</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.h2database</groupId>
|
|
||||||
<artifactId>h2</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<finalName>${project.artifactId}</finalName>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.openapi.LlmOpenApiApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-modules</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<artifactId>llm-guard-open-api</artifactId>
|
|
||||||
|
|
||||||
<description>
|
|
||||||
对外开放接口模块
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-datasource</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-log</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-swagger</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-security</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.llm.guard</groupId>
|
|
||||||
<artifactId>llm-guard-common-mybatisplus</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.googlecode.aviator</groupId>
|
|
||||||
<artifactId>aviator</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.postgresql</groupId>
|
|
||||||
<artifactId>postgresql</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.h2database</groupId>
|
|
||||||
<artifactId>h2</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<finalName>${project.artifactId}</finalName>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.openapi.LlmOpenApiApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package com.llm.guard.openapi;
|
|
||||||
|
|
||||||
import com.llm.guard.common.security.annotation.EnableCustomConfig;
|
|
||||||
import com.llm.guard.common.security.annotation.EnableLmFeignClients;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
@EnableCustomConfig
|
|
||||||
@EnableLmFeignClients
|
|
||||||
@SpringBootApplication
|
|
||||||
public class LlmOpenApiApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(LlmOpenApiApplication.class, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
package com.llm.guard.openapi.controller;
|
|
||||||
|
|
||||||
import com.llm.guard.common.core.web.domain.AjaxResult;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardCheckRequest;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardRequest;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardResponse;
|
|
||||||
import com.llm.guard.openapi.service.OpenApiGuardService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestHeader;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/openapi/guard")
|
|
||||||
@Tag(name = "开放接口-防护检测")
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class OpenApiGuardController {
|
|
||||||
|
|
||||||
private final OpenApiGuardService openApiGuardService;
|
|
||||||
|
|
||||||
@Operation(summary = "统一防护检测接口(ACL/ATTACK/CONTENT)")
|
|
||||||
@PostMapping("/check")
|
|
||||||
public AjaxResult check(
|
|
||||||
@RequestHeader("X-Api-Key") String apiKey,
|
|
||||||
@RequestHeader("X-Api-Secret") String apiSecret,
|
|
||||||
@RequestBody OpenApiGuardCheckRequest request
|
|
||||||
) {
|
|
||||||
OpenApiGuardService.CallerAuthResult authResult = openApiGuardService.authenticate(apiKey, apiSecret);
|
|
||||||
if (!authResult.isSuccess()) {
|
|
||||||
return AjaxResult.error(401, authResult.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
Object reqData = request == null ? null : request.getReqData();
|
|
||||||
if (reqData == null) {
|
|
||||||
return AjaxResult.error(400, "reqData 不能为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenApiGuardRequest guardRequest = buildGuardRequest(request);
|
|
||||||
OpenApiGuardResponse response;
|
|
||||||
try {
|
|
||||||
response = openApiGuardService.check(guardRequest, authResult.getUserName());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return AjaxResult.error(400, e.getMessage());
|
|
||||||
}
|
|
||||||
return AjaxResult.success(buildMessage(response), response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OpenApiGuardRequest buildGuardRequest(OpenApiGuardCheckRequest request)
|
|
||||||
{
|
|
||||||
OpenApiGuardRequest guardRequest = new OpenApiGuardRequest();
|
|
||||||
guardRequest.setSourceIp(request.getSourceIp());
|
|
||||||
guardRequest.setPath(request.getPath());
|
|
||||||
guardRequest.setMethod(request.getMethod());
|
|
||||||
guardRequest.setInterfaceType(request.getInterfaceType());
|
|
||||||
guardRequest.setCallerId(request.getCallerId());
|
|
||||||
guardRequest.setPayload(request.getReqData());
|
|
||||||
|
|
||||||
if (request.getReqData() instanceof Map<?, ?> map)
|
|
||||||
{
|
|
||||||
Map<String, Object> extensions = new LinkedHashMap<>();
|
|
||||||
for (Map.Entry<?, ?> entry : map.entrySet())
|
|
||||||
{
|
|
||||||
extensions.put(String.valueOf(entry.getKey()), entry.getValue());
|
|
||||||
}
|
|
||||||
guardRequest.setExtensions(extensions);
|
|
||||||
}
|
|
||||||
return guardRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildMessage(OpenApiGuardResponse response)
|
|
||||||
{
|
|
||||||
if (Boolean.FALSE.equals(response.getAlerted()) || response.getHits() == null || response.getHits().isEmpty())
|
|
||||||
{
|
|
||||||
return "未命中规则,允许通过";
|
|
||||||
}
|
|
||||||
Set<String> messages = new LinkedHashSet<>();
|
|
||||||
response.getHits().forEach(hit -> {
|
|
||||||
if (hit.getMessage() != null && !hit.getMessage().isBlank())
|
|
||||||
{
|
|
||||||
messages.add(hit.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (messages.isEmpty())
|
|
||||||
{
|
|
||||||
return "命中规则";
|
|
||||||
}
|
|
||||||
return String.join(";", messages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package com.llm.guard.openapi.domain;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Schema(description = "开放防护检测请求包装")
|
|
||||||
public class OpenApiGuardCheckRequest {
|
|
||||||
|
|
||||||
@Schema(description = "来源IP(ACL IP 规则依赖)", requiredMode = Schema.RequiredMode.REQUIRED)
|
|
||||||
private String sourceIp;
|
|
||||||
|
|
||||||
@Schema(description = "请求路径(ACL 接口规则依赖)", requiredMode = Schema.RequiredMode.REQUIRED)
|
|
||||||
private String path;
|
|
||||||
|
|
||||||
@Schema(description = "请求方法(ACL 接口规则依赖)", requiredMode = Schema.RequiredMode.REQUIRED)
|
|
||||||
private String method;
|
|
||||||
|
|
||||||
@Schema(description = "接口类型(AGENT/LLM/VLM 分流)", requiredMode = Schema.RequiredMode.REQUIRED)
|
|
||||||
private String interfaceType;
|
|
||||||
|
|
||||||
@Schema(description = "调用方标识(调用方追踪)", requiredMode = Schema.RequiredMode.REQUIRED)
|
|
||||||
private String callerId;
|
|
||||||
|
|
||||||
@Schema(description = "业务请求体(固定字段,透传网关原始入参)", requiredMode = Schema.RequiredMode.REQUIRED)
|
|
||||||
private Object reqData;
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
package com.llm.guard.openapi.domain;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Schema(description = "开放防护检测请求")
|
|
||||||
public class OpenApiGuardRequest {
|
|
||||||
|
|
||||||
@Schema(description = "请求ID,调用方可自定义")
|
|
||||||
private String requestId;
|
|
||||||
|
|
||||||
@Schema(description = "链路追踪ID")
|
|
||||||
private String traceId;
|
|
||||||
|
|
||||||
@Schema(description = "作用域编码,对应规则scopeCode")
|
|
||||||
private String scopeCode;
|
|
||||||
|
|
||||||
@Schema(description = "接口类型: AGENT/LLM/VLM")
|
|
||||||
private String interfaceType;
|
|
||||||
|
|
||||||
@Schema(description = "请求路径")
|
|
||||||
private String path;
|
|
||||||
|
|
||||||
@Schema(description = "请求方法")
|
|
||||||
private String method;
|
|
||||||
|
|
||||||
@Schema(description = "来源IP")
|
|
||||||
private String sourceIp;
|
|
||||||
|
|
||||||
@Schema(description = "调用方标识")
|
|
||||||
private String callerId;
|
|
||||||
|
|
||||||
@Schema(description = "是否流式")
|
|
||||||
private Boolean stream;
|
|
||||||
|
|
||||||
@Schema(description = "智能体会话文本")
|
|
||||||
private String text;
|
|
||||||
|
|
||||||
@Schema(description = "消息体(兼容语义模型/多模态)")
|
|
||||||
private List<Map<String, Object>> messages;
|
|
||||||
|
|
||||||
@Schema(description = "文件列表")
|
|
||||||
private List<Map<String, Object>> files;
|
|
||||||
|
|
||||||
@Schema(description = "附加元数据")
|
|
||||||
private Map<String, Object> metadata;
|
|
||||||
|
|
||||||
@Schema(description = "原始请求体,支持任意结构")
|
|
||||||
private Object payload;
|
|
||||||
|
|
||||||
@Schema(description = "扩展字段(透传任意入参)")
|
|
||||||
private Map<String, Object> extensions = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
@JsonAnySetter
|
|
||||||
public void putExtension(String key, Object value) {
|
|
||||||
this.extensions.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package com.llm.guard.openapi.domain;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Schema(description = "开放防护检测响应")
|
|
||||||
public class OpenApiGuardResponse {
|
|
||||||
|
|
||||||
@Schema(description = "请求ID")
|
|
||||||
private String requestId;
|
|
||||||
|
|
||||||
@Schema(description = "链路ID")
|
|
||||||
private String traceId;
|
|
||||||
|
|
||||||
@Schema(description = "最终决策: ALLOW/BLOCK")
|
|
||||||
private String decision;
|
|
||||||
|
|
||||||
@Schema(description = "是否触发告警")
|
|
||||||
private Boolean alerted;
|
|
||||||
|
|
||||||
@Schema(description = "命中列表")
|
|
||||||
private List<OpenApiHitResp> hits = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package com.llm.guard.openapi.domain;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Schema(description = "命中明细")
|
|
||||||
public class OpenApiHitResp {
|
|
||||||
|
|
||||||
@Schema(description = "模块: ACL/ATTACK/CONTENT")
|
|
||||||
private String moduleType;
|
|
||||||
|
|
||||||
@Schema(description = "事件类型")
|
|
||||||
private String eventType;
|
|
||||||
|
|
||||||
@Schema(description = "规则ID")
|
|
||||||
private String ruleId;
|
|
||||||
|
|
||||||
@Schema(description = "规则编码")
|
|
||||||
private String ruleCode;
|
|
||||||
|
|
||||||
@Schema(description = "动作: ALLOW/BLOCK/ALERT/MASK/REPLACE")
|
|
||||||
private String action;
|
|
||||||
|
|
||||||
@Schema(description = "命中说明")
|
|
||||||
private String message;
|
|
||||||
}
|
|
||||||
|
|
@ -1,801 +0,0 @@
|
||||||
package com.llm.guard.openapi.service;
|
|
||||||
|
|
||||||
import com.googlecode.aviator.AviatorEvaluator;
|
|
||||||
import com.llm.guard.common.core.utils.StringUtils;
|
|
||||||
import com.llm.guard.common.core.utils.uuid.IdUtils;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardRequest;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardResponse;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiHitResp;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.jdbc.core.PreparedStatementCreator;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
|
||||||
import org.springframework.jdbc.support.KeyHolder;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.Date;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class OpenApiGuardService {
|
|
||||||
|
|
||||||
private static final String ENABLED = "ENABLED";
|
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
|
||||||
|
|
||||||
public CallerAuthResult authenticate(String apiKey, String apiSecret) {
|
|
||||||
if (StringUtils.isAnyBlank(apiKey, apiSecret)) {
|
|
||||||
return new CallerAuthResult(false, null, "apiKey/apiSecret 不能为空");
|
|
||||||
}
|
|
||||||
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
|
|
||||||
"SELECT user_id, user_name, status, del_flag, api_secret FROM d_sys_user WHERE api_key = ? LIMIT 1",
|
|
||||||
apiKey
|
|
||||||
);
|
|
||||||
if (rows.isEmpty()) {
|
|
||||||
return new CallerAuthResult(false, null, "apiKey 不存在");
|
|
||||||
}
|
|
||||||
Map<String, Object> row = rows.get(0);
|
|
||||||
String status = str(row, "status");
|
|
||||||
String delFlag = str(row, "del_flag");
|
|
||||||
if (!"0".equals(status) || !"0".equals(delFlag)) {
|
|
||||||
return new CallerAuthResult(false, null, "账号不可用");
|
|
||||||
}
|
|
||||||
String dbSecret = str(row, "api_secret");
|
|
||||||
if (!Objects.equals(dbSecret, apiSecret)) {
|
|
||||||
return new CallerAuthResult(false, null, "apiSecret 错误");
|
|
||||||
}
|
|
||||||
return new CallerAuthResult(true, str(row, "user_name"), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenApiGuardResponse check(OpenApiGuardRequest request, String apiCaller) {
|
|
||||||
normalizeRequest(request);
|
|
||||||
validateRequiredFields(request);
|
|
||||||
OpenApiGuardResponse response = new OpenApiGuardResponse();
|
|
||||||
response.setRequestId(request.getRequestId());
|
|
||||||
response.setTraceId(request.getTraceId());
|
|
||||||
response.setDecision("ALLOW");
|
|
||||||
response.setAlerted(Boolean.FALSE);
|
|
||||||
|
|
||||||
String corpus = buildCorpus(request).toLowerCase(Locale.ROOT);
|
|
||||||
List<MatchHit> hits = new ArrayList<>();
|
|
||||||
|
|
||||||
hits.addAll(checkAclRules(request, corpus));
|
|
||||||
hits.addAll(checkAttackRules(request, corpus));
|
|
||||||
hits.addAll(checkContentRules(request, corpus));
|
|
||||||
|
|
||||||
if (!hits.isEmpty()) {
|
|
||||||
response.setAlerted(Boolean.TRUE);
|
|
||||||
for (MatchHit hit : hits) {
|
|
||||||
response.getHits().add(new OpenApiHitResp(hit.moduleType, hit.eventType, hit.ruleId, hit.ruleCode, hit.action, hit.message));
|
|
||||||
}
|
|
||||||
boolean blocked = hits.stream().anyMatch(hit -> "BLOCK".equalsIgnoreCase(hit.action));
|
|
||||||
response.setDecision(blocked ? "BLOCK" : "ALLOW");
|
|
||||||
saveAlertLogs(request, apiCaller, hits);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void normalizeRequest(OpenApiGuardRequest request) {
|
|
||||||
if (StringUtils.isBlank(request.getRequestId())) {
|
|
||||||
request.setRequestId(firstString(request, "requestId", "request_id", "reqId", "req_id"));
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getRequestId())) {
|
|
||||||
request.setRequestId(IdUtils.fastSimpleUUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(request.getTraceId())) {
|
|
||||||
request.setTraceId(firstString(request, "traceId", "trace_id"));
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getTraceId())) {
|
|
||||||
request.setTraceId(IdUtils.fastSimpleUUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(request.getScopeCode())) {
|
|
||||||
request.setScopeCode(firstString(request, "scopeCode", "scope_code", "scope"));
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getScopeCode())) {
|
|
||||||
request.setScopeCode("GLOBAL");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(request.getInterfaceType())) {
|
|
||||||
request.setInterfaceType(firstString(request, "interfaceType", "interface_type", "sceneType", "scene_type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(request.getPath())) {
|
|
||||||
request.setPath(firstString(request, "path", "requestPath", "request_path", "uri", "url"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(request.getMethod())) {
|
|
||||||
request.setMethod(firstString(request, "method", "httpMethod", "http_method", "requestMethod", "request_method"));
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getSourceIp())) {
|
|
||||||
request.setSourceIp(firstString(request, "sourceIp", "source_ip", "clientIp", "client_ip", "remoteIp", "remote_ip", "requestIp", "request_ip", "ip"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(request.getCallerId())) {
|
|
||||||
request.setCallerId(firstString(request, "callerId", "caller_id", "appId", "app_id", "clientId", "client_id", "tenantId", "tenant_id"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(request.getText())) {
|
|
||||||
request.setText(firstString(request, "text", "prompt", "query", "input", "content"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.getStream() == null) {
|
|
||||||
request.setStream(firstBoolean(request, "stream", "isStream", "is_stream"));
|
|
||||||
}
|
|
||||||
if (request.getStream() == null) {
|
|
||||||
request.setStream(Boolean.FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateRequiredFields(OpenApiGuardRequest request) {
|
|
||||||
List<String> missing = new ArrayList<>();
|
|
||||||
if (StringUtils.isBlank(request.getSourceIp())) {
|
|
||||||
missing.add("sourceIp");
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getPath())) {
|
|
||||||
missing.add("path");
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getMethod())) {
|
|
||||||
missing.add("method");
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getInterfaceType())) {
|
|
||||||
missing.add("interfaceType");
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(request.getCallerId())) {
|
|
||||||
missing.add("callerId");
|
|
||||||
}
|
|
||||||
if (!missing.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("reqData 缺少必填字段: " + String.join(", ", missing));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<MatchHit> checkAclRules(OpenApiGuardRequest request, String corpus) {
|
|
||||||
List<MatchHit> hits = new ArrayList<>();
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(request.getSourceIp())) {
|
|
||||||
List<Map<String, Object>> ipRules = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, list_type, ip_type, ip_value FROM d_acl_ip_rule WHERE scope_code = ? AND status = ? AND is_deleted = 0 ORDER BY priority ASC",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
List<Map<String, Object>> whiteListRules = ipRules.stream()
|
|
||||||
.filter(row -> "WHITELIST".equalsIgnoreCase(str(row, "list_type")))
|
|
||||||
.toList();
|
|
||||||
boolean whiteMatched = whiteListRules.isEmpty();
|
|
||||||
for (Map<String, Object> row : ipRules) {
|
|
||||||
String ipType = str(row, "ip_type");
|
|
||||||
String ipValue = str(row, "ip_value");
|
|
||||||
if (!matchIp(request.getSourceIp(), ipType, ipValue)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String listType = str(row, "list_type");
|
|
||||||
if ("WHITELIST".equalsIgnoreCase(listType)) {
|
|
||||||
whiteMatched = true;
|
|
||||||
} else if ("BLACKLIST".equalsIgnoreCase(listType)) {
|
|
||||||
hits.add(new MatchHit("ACL", "ACL_IP_BLACKLIST", str(row, "id"), null, "BLOCK", "命中IP黑名单规则"));
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!whiteMatched) {
|
|
||||||
hits.add(new MatchHit("ACL", "ACL_IP_WHITELIST", null, null, "BLOCK", "来源IP不在白名单"));
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(request.getPath())) {
|
|
||||||
List<Map<String, Object>> endpointRules = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, match_type, uri_pattern, methods FROM d_acl_endpoint_rule WHERE scope_code = ? AND status = ? AND is_deleted = 0",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
for (Map<String, Object> row : endpointRules) {
|
|
||||||
if (!matchMethod(request.getMethod(), str(row, "methods"))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (matchPath(request.getPath(), str(row, "match_type"), str(row, "uri_pattern"))) {
|
|
||||||
hits.add(new MatchHit("ACL", "ACL_ENDPOINT_BLOCK", str(row, "id"), null, "BLOCK", "命中接口封堵规则"));
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Map<String, Object>> customRules = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, rule_code, logic_op, action FROM d_acl_custom_rule WHERE scope_code = ? AND status = ? AND is_deleted = 0 ORDER BY priority ASC",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
Map<String, Object> env = buildAviatorEnv(request, corpus);
|
|
||||||
for (Map<String, Object> rule : customRules) {
|
|
||||||
List<Map<String, Object>> conds = jdbcTemplate.queryForList(
|
|
||||||
"SELECT condition_expr FROM d_acl_custom_condition WHERE rule_id = ? AND is_deleted = 0 ORDER BY seq_no ASC",
|
|
||||||
str(rule, "id")
|
|
||||||
);
|
|
||||||
if (conds.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String logicOp = str(rule, "logic_op");
|
|
||||||
boolean matched = "OR".equalsIgnoreCase(logicOp) ? false : true;
|
|
||||||
for (Map<String, Object> cond : conds) {
|
|
||||||
boolean one = evalCondition(str(cond, "condition_expr"), env);
|
|
||||||
if ("OR".equalsIgnoreCase(logicOp)) {
|
|
||||||
matched = matched || one;
|
|
||||||
} else {
|
|
||||||
matched = matched && one;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matched) {
|
|
||||||
String action = upperOrDefault(val(rule, "action"), "ALERT");
|
|
||||||
hits.add(new MatchHit("ACL", "ACL_CUSTOM_RULE", str(rule, "id"), str(rule, "rule_code"), action, "命中自定义组合规则"));
|
|
||||||
if ("BLOCK".equalsIgnoreCase(action)) {
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<MatchHit> checkAttackRules(OpenApiGuardRequest request, String corpus) {
|
|
||||||
List<MatchHit> hits = new ArrayList<>();
|
|
||||||
Map<String, Boolean> switches = loadAttackSwitches(request.getScopeCode());
|
|
||||||
Map<String, String> actionByRule = new HashMap<>();
|
|
||||||
|
|
||||||
List<Map<String, Object>> rules = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, rule_code, category, sub_type, action FROM d_attack_rule WHERE scope_code = ? AND status = ? AND is_deleted = 0",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
List<Map<String, Object>> signatures = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, sig_type, sig_value, rule_code FROM d_attack_signature WHERE scope_code = ? AND status = ? AND is_deleted = 0",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
|
|
||||||
for (Map<String, Object> rule : rules) {
|
|
||||||
String switchKey = buildSwitchKey(str(rule, "category"), str(rule, "sub_type"));
|
|
||||||
if (switches.containsKey(switchKey) && !Boolean.TRUE.equals(switches.get(switchKey))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
actionByRule.put(str(rule, "rule_code"), upperOrDefault(val(rule, "action"), "ALERT"));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map<String, Object> sig : signatures) {
|
|
||||||
String ruleCode = str(sig, "rule_code");
|
|
||||||
if (!actionByRule.containsKey(ruleCode)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (matchSignature(corpus, str(sig, "sig_type"), str(sig, "sig_value"))) {
|
|
||||||
String action = actionByRule.get(ruleCode);
|
|
||||||
hits.add(new MatchHit("ATTACK", "ATTACK_SIGNATURE", str(sig, "id"), ruleCode, action, "命中攻击特征"));
|
|
||||||
if ("BLOCK".equalsIgnoreCase(action)) {
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<MatchHit> checkContentRules(OpenApiGuardRequest request, String corpus) {
|
|
||||||
List<MatchHit> hits = new ArrayList<>();
|
|
||||||
|
|
||||||
List<Map<String, Object>> dlpRules = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, rule_code, data_type, action FROM d_content_dlp_rule WHERE scope_code = ? AND status = ? AND is_deleted = 0",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
for (Map<String, Object> rule : dlpRules) {
|
|
||||||
if (matchDataType(corpus, str(rule, "data_type"))) {
|
|
||||||
String action = upperOrDefault(val(rule, "action"), "ALERT");
|
|
||||||
hits.add(new MatchHit("CONTENT", "CONTENT_DLP", str(rule, "id"), str(rule, "rule_code"), action, "命中内容DLP规则"));
|
|
||||||
if ("BLOCK".equalsIgnoreCase(action)) {
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Map<String, Object>> contentPolicies = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, policy_code, action FROM d_content_policy WHERE scope_code = ? AND status = ? AND is_deleted = 0",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
for (Map<String, Object> policy : contentPolicies) {
|
|
||||||
String policyCode = str(policy, "policy_code");
|
|
||||||
if (StringUtils.isNotBlank(policyCode) && corpus.contains(policyCode.toLowerCase(Locale.ROOT))) {
|
|
||||||
String action = upperOrDefault(val(policy, "action"), "ALERT");
|
|
||||||
hits.add(new MatchHit("CONTENT", "CONTENT_POLICY", str(policy, "id"), policyCode, action, "命中内容策略编码关键字"));
|
|
||||||
if ("BLOCK".equalsIgnoreCase(action)) {
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Map<String, Object>> maskPolicies = jdbcTemplate.queryForList(
|
|
||||||
"SELECT id, policy_code, template_name, action FROM d_content_mask_policy WHERE scope_code = ? AND status = ? AND is_deleted = 0",
|
|
||||||
request.getScopeCode(), ENABLED
|
|
||||||
);
|
|
||||||
for (Map<String, Object> policy : maskPolicies) {
|
|
||||||
String template = str(policy, "template_name");
|
|
||||||
if (StringUtils.isNotBlank(template) && corpus.contains(template.toLowerCase(Locale.ROOT))) {
|
|
||||||
String action = upperOrDefault(val(policy, "action"), "MASK");
|
|
||||||
hits.add(new MatchHit("CONTENT", "CONTENT_MASK", str(policy, "id"), str(policy, "policy_code"), action, "命中脱敏模板关键字"));
|
|
||||||
if ("BLOCK".equalsIgnoreCase(action)) {
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hits;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveAlertLogs(OpenApiGuardRequest request, String apiCaller, List<MatchHit> hits) {
|
|
||||||
String severity = resolveSeverity(request.getScopeCode());
|
|
||||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
|
||||||
Date partitionDate = Date.valueOf(LocalDate.now());
|
|
||||||
|
|
||||||
for (MatchHit hit : hits) {
|
|
||||||
String eventNo = "EVT_" + IdUtils.fastSimpleUUID();
|
|
||||||
Long eventId = insertAlertEventAndReturnId(
|
|
||||||
eventNo,
|
|
||||||
request,
|
|
||||||
hit,
|
|
||||||
severity,
|
|
||||||
now,
|
|
||||||
partitionDate,
|
|
||||||
apiCaller
|
|
||||||
);
|
|
||||||
if (eventId != null) {
|
|
||||||
jdbcTemplate.update(
|
|
||||||
"INSERT INTO d_log_alert_hit (event_id, hit_order, hit_target, hit_field, hit_operator, expected_value, actual_value_preview, confidence, create_by, create_time, update_by, update_time, is_deleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)",
|
|
||||||
eventId,
|
|
||||||
1,
|
|
||||||
hit.moduleType,
|
|
||||||
hit.ruleCode,
|
|
||||||
hit.eventType,
|
|
||||||
"rule_matched",
|
|
||||||
hit.message,
|
|
||||||
1.0,
|
|
||||||
"open-api",
|
|
||||||
now,
|
|
||||||
"open-api",
|
|
||||||
now
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long insertAlertEventAndReturnId(
|
|
||||||
String eventNo,
|
|
||||||
OpenApiGuardRequest request,
|
|
||||||
MatchHit hit,
|
|
||||||
String severity,
|
|
||||||
Timestamp now,
|
|
||||||
Date partitionDate,
|
|
||||||
String apiCaller
|
|
||||||
) {
|
|
||||||
String sql = "INSERT INTO d_log_alert_event (event_no, request_id, trace_id, scope_code, module_type, event_type, rule_type, rule_id, rule_code, severity, action_taken, hit_message, request_path, request_method, source_ip, caller_id, is_stream, alert_status, occurred_at, partition_date, create_by, create_time, update_by, update_time, is_deleted) " +
|
|
||||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)";
|
|
||||||
|
|
||||||
KeyHolder keyHolder = new GeneratedKeyHolder();
|
|
||||||
PreparedStatementCreator psc = (Connection conn) -> {
|
|
||||||
PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
|
|
||||||
int i = 1;
|
|
||||||
ps.setString(i++, eventNo);
|
|
||||||
ps.setString(i++, request.getRequestId());
|
|
||||||
ps.setString(i++, request.getTraceId());
|
|
||||||
ps.setString(i++, request.getScopeCode());
|
|
||||||
ps.setString(i++, hit.moduleType);
|
|
||||||
ps.setString(i++, hit.eventType);
|
|
||||||
ps.setString(i++, hit.eventType);
|
|
||||||
ps.setString(i++, hit.ruleId);
|
|
||||||
ps.setString(i++, hit.ruleCode);
|
|
||||||
ps.setString(i++, severity);
|
|
||||||
ps.setString(i++, upperOrDefault(hit.action, "ALERT"));
|
|
||||||
ps.setString(i++, hit.message);
|
|
||||||
ps.setString(i++, request.getPath());
|
|
||||||
ps.setString(i++, request.getMethod());
|
|
||||||
ps.setString(i++, request.getSourceIp());
|
|
||||||
ps.setString(i++, StringUtils.isNotBlank(request.getCallerId()) ? request.getCallerId() : apiCaller);
|
|
||||||
ps.setInt(i++, Boolean.TRUE.equals(request.getStream()) ? 1 : 0);
|
|
||||||
ps.setString(i++, "NEW");
|
|
||||||
ps.setTimestamp(i++, now);
|
|
||||||
ps.setDate(i++, partitionDate);
|
|
||||||
ps.setString(i++, "open-api");
|
|
||||||
ps.setTimestamp(i++, now);
|
|
||||||
ps.setString(i++, "open-api");
|
|
||||||
ps.setTimestamp(i, now);
|
|
||||||
return ps;
|
|
||||||
};
|
|
||||||
jdbcTemplate.update(psc, keyHolder);
|
|
||||||
return extractGeneratedId(keyHolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long extractGeneratedId(KeyHolder keyHolder) {
|
|
||||||
if (keyHolder == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Map<String, Object> one = keyHolder.getKeys();
|
|
||||||
Long oneId = extractIdFromMap(one);
|
|
||||||
if (oneId != null) {
|
|
||||||
return oneId;
|
|
||||||
}
|
|
||||||
if (keyHolder.getKeyList() != null) {
|
|
||||||
for (Map<String, Object> m : keyHolder.getKeyList()) {
|
|
||||||
Long id = extractIdFromMap(m);
|
|
||||||
if (id != null) {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long extractIdFromMap(Map<String, Object> map) {
|
|
||||||
if (map == null || map.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Object idObj = map.get("id");
|
|
||||||
if (idObj == null) {
|
|
||||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
||||||
if ("id".equalsIgnoreCase(entry.getKey())) {
|
|
||||||
idObj = entry.getValue();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (idObj instanceof Number n) {
|
|
||||||
return n.longValue();
|
|
||||||
}
|
|
||||||
if (idObj != null) {
|
|
||||||
try {
|
|
||||||
return Long.valueOf(String.valueOf(idObj));
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveSeverity(String scopeCode) {
|
|
||||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(
|
|
||||||
"SELECT alert_level FROM d_log_attack_policy WHERE scope_code = ? AND status = ? AND is_deleted = 0 ORDER BY update_time DESC LIMIT 1",
|
|
||||||
scopeCode, ENABLED
|
|
||||||
);
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
return "MEDIUM";
|
|
||||||
}
|
|
||||||
return upperOrDefault(val(list.get(0), "alert_level"), "MEDIUM");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Boolean> loadAttackSwitches(String scopeCode) {
|
|
||||||
List<Map<String, Object>> switches = jdbcTemplate.queryForList(
|
|
||||||
"SELECT switch_key, enabled FROM d_attack_switch WHERE scope_code = ? AND is_deleted = 0",
|
|
||||||
scopeCode
|
|
||||||
);
|
|
||||||
if (switches.isEmpty()) {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
Map<String, Boolean> map = new HashMap<>();
|
|
||||||
for (Map<String, Object> sw : switches) {
|
|
||||||
map.put(str(sw, "switch_key"), toBool(val(sw, "enabled")));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> buildAviatorEnv(OpenApiGuardRequest request, String corpus) {
|
|
||||||
Map<String, Object> env = new HashMap<>();
|
|
||||||
env.put("requestId", request.getRequestId());
|
|
||||||
env.put("traceId", request.getTraceId());
|
|
||||||
env.put("scopeCode", request.getScopeCode());
|
|
||||||
env.put("interfaceType", request.getInterfaceType());
|
|
||||||
env.put("path", request.getPath());
|
|
||||||
env.put("method", request.getMethod());
|
|
||||||
env.put("sourceIp", request.getSourceIp());
|
|
||||||
env.put("callerId", request.getCallerId());
|
|
||||||
env.put("stream", request.getStream());
|
|
||||||
env.put("text", request.getText());
|
|
||||||
env.put("extensions", request.getExtensions());
|
|
||||||
env.put("corpus", corpus);
|
|
||||||
env.put("metadata", request.getMetadata());
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean evalCondition(String expr, Map<String, Object> env) {
|
|
||||||
try {
|
|
||||||
Object ret = AviatorEvaluator.execute(expr, env, true);
|
|
||||||
if (ret instanceof Boolean b) {
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchSignature(String corpus, String sigType, String sigValue) {
|
|
||||||
if (StringUtils.isBlank(sigValue)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if ("REGEX".equalsIgnoreCase(sigType)) {
|
|
||||||
return Pattern.compile(sigValue, Pattern.CASE_INSENSITIVE | Pattern.DOTALL).matcher(corpus).find();
|
|
||||||
}
|
|
||||||
return corpus.contains(sigValue.toLowerCase(Locale.ROOT));
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchDataType(String corpus, String dataType) {
|
|
||||||
if (StringUtils.isBlank(dataType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String t = dataType.toUpperCase(Locale.ROOT);
|
|
||||||
return switch (t) {
|
|
||||||
case "EMAIL" -> Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}").matcher(corpus).find();
|
|
||||||
case "PHONE" -> Pattern.compile("(?:\\+?86[- ]?)?1[3-9]\\d{9}").matcher(corpus).find();
|
|
||||||
case "ID_CARD" -> Pattern.compile("[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]").matcher(corpus).find();
|
|
||||||
case "BANK_CARD" -> Pattern.compile("\\b(?:\\d[ -]*?){13,19}\\b").matcher(corpus).find();
|
|
||||||
default -> corpus.contains(dataType.toLowerCase(Locale.ROOT));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchIp(String sourceIp, String ipType, String ipValue) {
|
|
||||||
if (StringUtils.isAnyBlank(sourceIp, ipType, ipValue)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return switch (ipType.toUpperCase(Locale.ROOT)) {
|
|
||||||
case "SINGLE" -> Objects.equals(sourceIp, ipValue);
|
|
||||||
case "REGEX" -> Pattern.compile(ipValue).matcher(sourceIp).find();
|
|
||||||
case "CIDR" -> matchCidr(sourceIp, ipValue);
|
|
||||||
default -> false;
|
|
||||||
};
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchCidr(String ip, String cidr) {
|
|
||||||
String[] parts = cidr.split("/");
|
|
||||||
if (parts.length != 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
long ipNum = ipv4ToLong(ip);
|
|
||||||
long baseNum = ipv4ToLong(parts[0]);
|
|
||||||
int prefix = Integer.parseInt(parts[1]);
|
|
||||||
long mask = prefix == 0 ? 0 : 0xFFFFFFFFL << (32 - prefix);
|
|
||||||
return (ipNum & mask) == (baseNum & mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long ipv4ToLong(String ip) {
|
|
||||||
String[] arr = ip.split("\\.");
|
|
||||||
if (arr.length != 4) {
|
|
||||||
throw new IllegalArgumentException("invalid ipv4");
|
|
||||||
}
|
|
||||||
long n = 0;
|
|
||||||
for (String s : arr) {
|
|
||||||
n = (n << 8) + Integer.parseInt(s);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchMethod(String reqMethod, String configuredMethods) {
|
|
||||||
if (StringUtils.isBlank(configuredMethods)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
String m = reqMethod == null ? "" : reqMethod.toUpperCase(Locale.ROOT);
|
|
||||||
String cfg = configuredMethods.toUpperCase(Locale.ROOT);
|
|
||||||
if ("ALL".equals(cfg)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (String item : cfg.split(",")) {
|
|
||||||
if (m.equals(item.trim())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchPath(String path, String matchType, String pattern) {
|
|
||||||
if (StringUtils.isAnyBlank(path, matchType, pattern)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return switch (matchType.toUpperCase(Locale.ROOT)) {
|
|
||||||
case "EXACT" -> Objects.equals(path, pattern);
|
|
||||||
case "PREFIX" -> path.startsWith(pattern);
|
|
||||||
case "REGEX" -> Pattern.compile(pattern).matcher(path).find();
|
|
||||||
default -> false;
|
|
||||||
};
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildSwitchKey(String category, String subType) {
|
|
||||||
String c = StringUtils.isBlank(category) ? "ATTACK" : category.toUpperCase(Locale.ROOT);
|
|
||||||
String s = StringUtils.isBlank(subType) ? "DEFAULT" : subType.toUpperCase(Locale.ROOT);
|
|
||||||
return c + "_" + s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildCorpus(OpenApiGuardRequest request) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
appendIfPresent(sb, request.getText());
|
|
||||||
appendIfPresent(sb, request.getPath());
|
|
||||||
appendIfPresent(sb, request.getMethod());
|
|
||||||
appendIfPresent(sb, request.getCallerId());
|
|
||||||
|
|
||||||
if (request.getMessages() != null) {
|
|
||||||
for (Map<String, Object> msg : request.getMessages()) {
|
|
||||||
appendFromObject(sb, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (request.getFiles() != null) {
|
|
||||||
for (Map<String, Object> file : request.getFiles()) {
|
|
||||||
appendFromObject(sb, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appendFromObject(sb, request.getMetadata());
|
|
||||||
appendFromObject(sb, request.getPayload());
|
|
||||||
appendFromObject(sb, request.getExtensions());
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String firstString(OpenApiGuardRequest request, String... aliases) {
|
|
||||||
Object v = findInExtensions(request, aliases);
|
|
||||||
if (v == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String s = String.valueOf(v);
|
|
||||||
return StringUtils.isBlank(s) ? null : s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean firstBoolean(OpenApiGuardRequest request, String... aliases) {
|
|
||||||
Object v = findInExtensions(request, aliases);
|
|
||||||
if (v == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return toBool(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object findInExtensions(OpenApiGuardRequest request, String... aliases) {
|
|
||||||
if (request == null || request.getExtensions() == null || request.getExtensions().isEmpty() || aliases == null || aliases.length == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Set<String> names = new HashSet<>();
|
|
||||||
for (String alias : aliases) {
|
|
||||||
if (StringUtils.isNotBlank(alias)) {
|
|
||||||
names.add(alias.toLowerCase(Locale.ROOT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return findValueByName(request.getExtensions(), names);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object findValueByName(Object node, Set<String> aliasNames) {
|
|
||||||
if (node == null || aliasNames == null || aliasNames.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (node instanceof Map<?, ?> map) {
|
|
||||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
|
||||||
String key = String.valueOf(entry.getKey());
|
|
||||||
if (aliasNames.contains(key.toLowerCase(Locale.ROOT))) {
|
|
||||||
return entry.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
|
||||||
Object nested = findValueByName(entry.getValue(), aliasNames);
|
|
||||||
if (nested != null) {
|
|
||||||
return nested;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (node instanceof Iterable<?> iterable) {
|
|
||||||
for (Object item : iterable) {
|
|
||||||
Object nested = findValueByName(item, aliasNames);
|
|
||||||
if (nested != null) {
|
|
||||||
return nested;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendFromObject(StringBuilder sb, Object obj) {
|
|
||||||
if (obj == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (obj instanceof String s) {
|
|
||||||
appendIfPresent(sb, s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (obj instanceof Map<?, ?> map) {
|
|
||||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
|
||||||
appendIfPresent(sb, String.valueOf(entry.getKey()));
|
|
||||||
appendFromObject(sb, entry.getValue());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (obj instanceof Iterable<?> iterable) {
|
|
||||||
for (Object item : iterable) {
|
|
||||||
appendFromObject(sb, item);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
appendIfPresent(sb, String.valueOf(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendIfPresent(StringBuilder sb, String value) {
|
|
||||||
if (StringUtils.isNotBlank(value)) {
|
|
||||||
sb.append(value).append(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean toBool(Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (value instanceof Boolean b) {
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
if (value instanceof Number n) {
|
|
||||||
return n.intValue() == 1;
|
|
||||||
}
|
|
||||||
return "1".equals(String.valueOf(value)) || "true".equalsIgnoreCase(String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String upperOrDefault(Object value, String defVal) {
|
|
||||||
String s = objToStr(value);
|
|
||||||
return StringUtils.isBlank(s) ? defVal : s.toUpperCase(Locale.ROOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String str(Map<String, Object> row, String key) {
|
|
||||||
return objToStr(val(row, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object val(Map<String, Object> row, String key) {
|
|
||||||
if (row.containsKey(key)) {
|
|
||||||
return row.get(key);
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, Object> entry : row.entrySet()) {
|
|
||||||
if (key.equalsIgnoreCase(entry.getKey())) {
|
|
||||||
return entry.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String objToStr(Object value) {
|
|
||||||
return value == null ? null : String.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public static class CallerAuthResult {
|
|
||||||
private boolean success;
|
|
||||||
private String userName;
|
|
||||||
private String message;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
private static class MatchHit {
|
|
||||||
private String moduleType;
|
|
||||||
private String eventType;
|
|
||||||
private String ruleId;
|
|
||||||
private String ruleCode;
|
|
||||||
private String action;
|
|
||||||
private String message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
server:
|
|
||||||
port: 6200
|
|
||||||
|
|
||||||
spring:
|
|
||||||
application:
|
|
||||||
name: llm-guard-open-api
|
|
||||||
profiles:
|
|
||||||
active: dev
|
|
||||||
cloud:
|
|
||||||
nacos:
|
|
||||||
username: ${NACOS_USERNAME:nacos}
|
|
||||||
password: ${NACOS_PASSWORD:nacos}
|
|
||||||
discovery:
|
|
||||||
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
|
|
||||||
namespace: ${NACOS_DISCOVERY_NAMESPACE:dev}
|
|
||||||
config:
|
|
||||||
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
|
|
||||||
namespace: ${NACOS_DISCOVERY_NAMESPACE:dev}
|
|
||||||
file-extension: yml
|
|
||||||
config:
|
|
||||||
import:
|
|
||||||
- optional:nacos:application-${spring.profiles.active}.yml
|
|
||||||
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yml
|
|
||||||
|
|
@ -1,231 +0,0 @@
|
||||||
package com.llm.guard.openapi;
|
|
||||||
|
|
||||||
import com.llm.guard.common.core.web.domain.AjaxResult;
|
|
||||||
import com.llm.guard.openapi.controller.OpenApiGuardController;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardCheckRequest;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardRequest;
|
|
||||||
import com.llm.guard.openapi.domain.OpenApiGuardResponse;
|
|
||||||
import com.llm.guard.openapi.service.OpenApiGuardService;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
class OpenApiGuardSimulationTest {
|
|
||||||
|
|
||||||
private JdbcTemplate jdbcTemplate;
|
|
||||||
private OpenApiGuardController controller;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
DataSource dataSource = new DriverManagerDataSource("jdbc:h2:mem:guard;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1", "sa", "");
|
|
||||||
jdbcTemplate = new JdbcTemplate(dataSource);
|
|
||||||
OpenApiGuardService service = new OpenApiGuardService(jdbcTemplate);
|
|
||||||
controller = new OpenApiGuardController(service);
|
|
||||||
|
|
||||||
initSchema();
|
|
||||||
seedAuthUser();
|
|
||||||
seedRules();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_simulate_three_gateway_requests_and_verify_alerts() {
|
|
||||||
OpenApiGuardRequest agentReq = new OpenApiGuardRequest();
|
|
||||||
agentReq.setRequestId("req-agent-1");
|
|
||||||
agentReq.setTraceId("trace-agent-1");
|
|
||||||
agentReq.setScopeCode("GLOBAL");
|
|
||||||
agentReq.setInterfaceType("AGENT");
|
|
||||||
agentReq.setCallerId("partnerA");
|
|
||||||
agentReq.setPath("/api/session/run");
|
|
||||||
agentReq.setMethod("POST");
|
|
||||||
agentReq.setSourceIp("10.0.0.1");
|
|
||||||
agentReq.setText("你好");
|
|
||||||
AjaxResult agentResult = controller.check("ak_test", "sk_test", wrap(agentReq));
|
|
||||||
|
|
||||||
assertEquals(200, agentResult.get("code"));
|
|
||||||
OpenApiGuardResponse agentResp = (OpenApiGuardResponse) agentResult.get("data");
|
|
||||||
assertEquals("BLOCK", agentResp.getDecision());
|
|
||||||
assertTrue(agentResp.getAlerted());
|
|
||||||
assertEquals("ACL", agentResp.getHits().get(0).getModuleType());
|
|
||||||
assertEquals(1, countAlertByRequestId("req-agent-1"));
|
|
||||||
|
|
||||||
OpenApiGuardRequest vlmReq = new OpenApiGuardRequest();
|
|
||||||
vlmReq.setRequestId("req-vlm-1");
|
|
||||||
vlmReq.setTraceId("trace-vlm-1");
|
|
||||||
vlmReq.setScopeCode("GLOBAL");
|
|
||||||
vlmReq.setInterfaceType("VLM");
|
|
||||||
vlmReq.setCallerId("partnerA");
|
|
||||||
vlmReq.setPath("/api/vlm/chat/completion/V2");
|
|
||||||
vlmReq.setMethod("POST");
|
|
||||||
vlmReq.setSourceIp("10.0.0.1");
|
|
||||||
vlmReq.setMessages(List.of(Map.of("role", "user", "content", "please union select password")));
|
|
||||||
AjaxResult vlmResult = controller.check("ak_test", "sk_test", wrap(vlmReq));
|
|
||||||
|
|
||||||
assertEquals(200, vlmResult.get("code"));
|
|
||||||
OpenApiGuardResponse vlmResp = (OpenApiGuardResponse) vlmResult.get("data");
|
|
||||||
assertEquals("BLOCK", vlmResp.getDecision());
|
|
||||||
assertTrue(vlmResp.getAlerted());
|
|
||||||
assertEquals("ATTACK", vlmResp.getHits().get(0).getModuleType());
|
|
||||||
assertEquals(1, countAlertByRequestId("req-vlm-1"));
|
|
||||||
|
|
||||||
OpenApiGuardRequest llmReq = new OpenApiGuardRequest();
|
|
||||||
llmReq.setRequestId("req-llm-1");
|
|
||||||
llmReq.setTraceId("trace-llm-1");
|
|
||||||
llmReq.setScopeCode("GLOBAL");
|
|
||||||
llmReq.setInterfaceType("LLM");
|
|
||||||
llmReq.setCallerId("partnerA");
|
|
||||||
llmReq.setPath("/api/llm/chat/completion/V2");
|
|
||||||
llmReq.setMethod("POST");
|
|
||||||
llmReq.setSourceIp("10.0.0.1");
|
|
||||||
llmReq.setMessages(List.of(Map.of("role", "user", "content", "我的邮箱是 test_user@demo.com")));
|
|
||||||
AjaxResult llmResult = controller.check("ak_test", "sk_test", wrap(llmReq));
|
|
||||||
|
|
||||||
assertEquals(200, llmResult.get("code"));
|
|
||||||
OpenApiGuardResponse llmResp = (OpenApiGuardResponse) llmResult.get("data");
|
|
||||||
assertEquals("ALLOW", llmResp.getDecision());
|
|
||||||
assertTrue(llmResp.getAlerted());
|
|
||||||
assertEquals("CONTENT", llmResp.getHits().get(0).getModuleType());
|
|
||||||
assertEquals(1, countAlertByRequestId("req-llm-1"));
|
|
||||||
|
|
||||||
Integer totalEvent = jdbcTemplate.queryForObject("select count(1) from d_log_alert_event", Integer.class);
|
|
||||||
Integer totalHit = jdbcTemplate.queryForObject("select count(1) from d_log_alert_hit", Integer.class);
|
|
||||||
assertEquals(3, totalEvent);
|
|
||||||
assertEquals(3, totalHit);
|
|
||||||
|
|
||||||
OpenApiGuardRequest cleanReq = new OpenApiGuardRequest();
|
|
||||||
cleanReq.setRequestId("req-clean-1");
|
|
||||||
cleanReq.setTraceId("trace-clean-1");
|
|
||||||
cleanReq.setScopeCode("GLOBAL");
|
|
||||||
cleanReq.setInterfaceType("LLM");
|
|
||||||
cleanReq.setCallerId("partnerA");
|
|
||||||
cleanReq.setPath("/api/normal");
|
|
||||||
cleanReq.setMethod("POST");
|
|
||||||
cleanReq.setSourceIp("10.0.0.1");
|
|
||||||
cleanReq.setText("hello world");
|
|
||||||
AjaxResult cleanResult = controller.check("ak_test", "sk_test", wrap(cleanReq));
|
|
||||||
OpenApiGuardResponse cleanResp = (OpenApiGuardResponse) cleanResult.get("data");
|
|
||||||
assertFalse(cleanResp.getAlerted());
|
|
||||||
assertEquals("ALLOW", cleanResp.getDecision());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_accept_raw_gateway_payload_without_fixed_fields() {
|
|
||||||
OpenApiGuardRequest rawReq = new OpenApiGuardRequest();
|
|
||||||
rawReq.setInterfaceType("LLM");
|
|
||||||
rawReq.setCallerId("partnerA");
|
|
||||||
rawReq.setPath("/api/llm/chat/completion/V2");
|
|
||||||
rawReq.setMethod("POST");
|
|
||||||
rawReq.setSourceIp("10.0.0.1");
|
|
||||||
rawReq.getExtensions().put("requestId", "req-raw-1");
|
|
||||||
rawReq.getExtensions().put("traceId", "trace-raw-1");
|
|
||||||
rawReq.getExtensions().put("scopeCode", "GLOBAL");
|
|
||||||
rawReq.getExtensions().put("messages", List.of(Map.of("role", "user", "content", "please union select test_user@demo.com")));
|
|
||||||
AjaxResult rawResult = controller.check("ak_test", "sk_test", wrap(rawReq));
|
|
||||||
|
|
||||||
assertEquals(200, rawResult.get("code"));
|
|
||||||
OpenApiGuardResponse rawResp = (OpenApiGuardResponse) rawResult.get("data");
|
|
||||||
assertTrue(rawResp.getAlerted());
|
|
||||||
assertEquals("BLOCK", rawResp.getDecision());
|
|
||||||
assertTrue(rawResp.getHits().stream().noneMatch(h -> "ACL_IP_WHITELIST".equals(h.getEventType())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer countAlertByRequestId(String requestId) {
|
|
||||||
return jdbcTemplate.queryForObject("select count(1) from d_log_alert_event where request_id = ?", Integer.class, requestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OpenApiGuardCheckRequest wrap(OpenApiGuardRequest reqData) {
|
|
||||||
OpenApiGuardCheckRequest req = new OpenApiGuardCheckRequest();
|
|
||||||
req.setSourceIp(reqData.getSourceIp());
|
|
||||||
req.setPath(reqData.getPath());
|
|
||||||
req.setMethod(reqData.getMethod());
|
|
||||||
req.setInterfaceType(reqData.getInterfaceType());
|
|
||||||
req.setCallerId(reqData.getCallerId());
|
|
||||||
req.setReqData(toMap(reqData));
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> toMap(OpenApiGuardRequest reqData) {
|
|
||||||
Map<String, Object> m = new LinkedHashMap<>();
|
|
||||||
putIfNotNull(m, "requestId", reqData.getRequestId());
|
|
||||||
putIfNotNull(m, "traceId", reqData.getTraceId());
|
|
||||||
putIfNotNull(m, "scopeCode", reqData.getScopeCode());
|
|
||||||
putIfNotNull(m, "interfaceType", reqData.getInterfaceType());
|
|
||||||
putIfNotNull(m, "path", reqData.getPath());
|
|
||||||
putIfNotNull(m, "method", reqData.getMethod());
|
|
||||||
putIfNotNull(m, "sourceIp", reqData.getSourceIp());
|
|
||||||
putIfNotNull(m, "callerId", reqData.getCallerId());
|
|
||||||
putIfNotNull(m, "stream", reqData.getStream());
|
|
||||||
putIfNotNull(m, "text", reqData.getText());
|
|
||||||
putIfNotNull(m, "messages", reqData.getMessages());
|
|
||||||
putIfNotNull(m, "files", reqData.getFiles());
|
|
||||||
putIfNotNull(m, "metadata", reqData.getMetadata());
|
|
||||||
putIfNotNull(m, "payload", reqData.getPayload());
|
|
||||||
if (reqData.getExtensions() != null && !reqData.getExtensions().isEmpty()) {
|
|
||||||
m.putAll(reqData.getExtensions());
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void putIfNotNull(Map<String, Object> map, String key, Object value) {
|
|
||||||
if (value != null) {
|
|
||||||
map.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initSchema() {
|
|
||||||
jdbcTemplate.execute("drop all objects");
|
|
||||||
|
|
||||||
jdbcTemplate.execute("create table d_sys_user (user_id varchar(64), user_name varchar(64), status char(1), del_flag char(1), api_key varchar(128), api_secret varchar(256))");
|
|
||||||
|
|
||||||
jdbcTemplate.execute("create table d_acl_ip_rule (id varchar(64), scope_code varchar(64), list_type varchar(16), ip_type varchar(16), ip_value varchar(255), priority int, status varchar(20), is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_acl_endpoint_rule (id varchar(64), scope_code varchar(64), match_type varchar(16), uri_pattern varchar(512), methods varchar(64), status varchar(20), is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_acl_custom_rule (id varchar(64), scope_code varchar(64), rule_code varchar(100), logic_op varchar(8), action varchar(20), priority int, status varchar(20), is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_acl_custom_condition (id bigint generated by default as identity primary key, rule_id varchar(64), seq_no int, condition_expr varchar(1024), is_deleted int)");
|
|
||||||
|
|
||||||
jdbcTemplate.execute("create table d_attack_switch (id bigint generated by default as identity primary key, scope_code varchar(64), switch_key varchar(64), enabled smallint, is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_attack_rule (id varchar(64), scope_code varchar(64), rule_code varchar(100), category varchar(20), sub_type varchar(64), action varchar(20), status varchar(20), is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_attack_signature (id varchar(64), scope_code varchar(64), sig_type varchar(20), sig_value varchar(1024), rule_code varchar(100), status varchar(20), is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_log_attack_policy (id varchar(64), scope_code varchar(64), alert_level varchar(20), status varchar(20), update_time timestamp, is_deleted int)");
|
|
||||||
|
|
||||||
jdbcTemplate.execute("create table d_content_policy (id varchar(64), scope_code varchar(64), policy_code varchar(100), action varchar(20), status varchar(20), is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_content_dlp_rule (id varchar(64), scope_code varchar(64), rule_code varchar(100), data_type varchar(64), action varchar(20), status varchar(20), is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_content_mask_policy (id varchar(64), scope_code varchar(64), policy_code varchar(100), template_name varchar(128), action varchar(20), status varchar(20), is_deleted int)");
|
|
||||||
|
|
||||||
jdbcTemplate.execute("create table d_log_alert_event (id bigint generated by default as identity primary key, event_no varchar(64), request_id varchar(64), trace_id varchar(64), scope_code varchar(64), module_type varchar(20), event_type varchar(32), rule_type varchar(32), rule_id varchar(64), rule_code varchar(100), severity varchar(20), action_taken varchar(20), hit_message varchar(512), request_path varchar(1024), request_method varchar(16), source_ip varchar(45), caller_id varchar(128), is_stream smallint, alert_status varchar(20), occurred_at timestamp, partition_date date, create_by varchar(64), create_time timestamp, update_by varchar(64), update_time timestamp, is_deleted int)");
|
|
||||||
jdbcTemplate.execute("create table d_log_alert_hit (id bigint generated by default as identity primary key, event_id bigint, hit_order int, hit_target varchar(32), hit_field varchar(255), hit_operator varchar(32), expected_value varchar(512), actual_value_preview varchar(512), confidence decimal(5,2), create_by varchar(64), create_time timestamp, update_by varchar(64), update_time timestamp, is_deleted int)");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void seedAuthUser() {
|
|
||||||
jdbcTemplate.update("insert into d_sys_user(user_id, user_name, status, del_flag, api_key, api_secret) values (?,?,?,?,?,?)",
|
|
||||||
"u1", "partnerA", "0", "0", "ak_test", "sk_test");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void seedRules() {
|
|
||||||
jdbcTemplate.update("insert into d_acl_ip_rule(id, scope_code, list_type, ip_type, ip_value, priority, status, is_deleted) values (?,?,?,?,?,?,?,?)",
|
|
||||||
"ipw1", "GLOBAL", "WHITELIST", "SINGLE", "10.0.0.1", 1, "ENABLED", 0);
|
|
||||||
jdbcTemplate.update("insert into d_acl_endpoint_rule(id, scope_code, match_type, uri_pattern, methods, status, is_deleted) values (?,?,?,?,?,?,?)",
|
|
||||||
"ep1", "GLOBAL", "EXACT", "/api/session/run", "POST", "ENABLED", 0);
|
|
||||||
|
|
||||||
jdbcTemplate.update("insert into d_attack_switch(scope_code, switch_key, enabled, is_deleted) values (?,?,?,?)",
|
|
||||||
"GLOBAL", "INJECTION_SQL", 1, 0);
|
|
||||||
jdbcTemplate.update("insert into d_attack_rule(id, scope_code, rule_code, category, sub_type, action, status, is_deleted) values (?,?,?,?,?,?,?,?)",
|
|
||||||
"ar1", "GLOBAL", "ATTACK_SQL_1", "INJECTION", "SQL", "BLOCK", "ENABLED", 0);
|
|
||||||
jdbcTemplate.update("insert into d_attack_signature(id, scope_code, sig_type, sig_value, rule_code, status, is_deleted) values (?,?,?,?,?,?,?)",
|
|
||||||
"sig1", "GLOBAL", "REGEX", "union\\s+select", "ATTACK_SQL_1", "ENABLED", 0);
|
|
||||||
|
|
||||||
jdbcTemplate.update("insert into d_content_dlp_rule(id, scope_code, rule_code, data_type, action, status, is_deleted) values (?,?,?,?,?,?,?)",
|
|
||||||
"dlp1", "GLOBAL", "DLP_EMAIL_1", "EMAIL", "ALERT", "ENABLED", 0);
|
|
||||||
|
|
||||||
jdbcTemplate.update("insert into d_log_attack_policy(id, scope_code, alert_level, status, update_time, is_deleted) values (?,?,?,?,CURRENT_TIMESTAMP,?)",
|
|
||||||
"lp1", "GLOBAL", "HIGH", "ENABLED", 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -60,32 +60,13 @@
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<version>3.3.0</version>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.system.LlmSystemApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -79,41 +79,17 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>com.llm.guard.system.LlmSystemApplication</mainClass>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- 把所有依赖 copy 到 lib/ 目录 -->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-dependencies</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-dependencies</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
|
||||||
<excludeTransitive>false</excludeTransitive>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package com.llm.guard.system.config;
|
|
||||||
|
|
||||||
import com.llm.guard.system.service.ISysUserService;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.boot.ApplicationArguments;
|
|
||||||
import org.springframework.boot.ApplicationRunner;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class UserApiCredentialInitRunner implements ApplicationRunner {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(UserApiCredentialInitRunner.class);
|
|
||||||
|
|
||||||
private final ISysUserService userService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(ApplicationArguments args) {
|
|
||||||
int count = userService.initUserApiCredentials();
|
|
||||||
if (count > 0) {
|
|
||||||
log.info("初始化用户开放接口密钥完成,补齐数量: {}", count);
|
|
||||||
} else {
|
|
||||||
log.info("用户开放接口密钥检查完成,无需补齐");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -142,21 +142,4 @@ public interface SysUserMapper extends BaseMapper<SysUser>
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public SysUser checkEmailUnique(String email);
|
public SysUser checkEmailUnique(String email);
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询 apiKey/apiSecret 缺失用户
|
|
||||||
*
|
|
||||||
* @return 用户列表
|
|
||||||
*/
|
|
||||||
public List<SysUser> selectUsersWithoutApiCredentials();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户开放接口密钥
|
|
||||||
*
|
|
||||||
* @param userId 用户ID
|
|
||||||
* @param apiKey apiKey
|
|
||||||
* @param apiSecret apiSecret
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
public int updateApiCredentials(@Param("userId") String userId, @Param("apiKey") String apiKey, @Param("apiSecret") String apiSecret);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,11 +212,4 @@ public interface ISysUserService extends IService<SysUser>
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
|
public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化缺失的开放接口密钥
|
|
||||||
*
|
|
||||||
* @return 初始化数量
|
|
||||||
*/
|
|
||||||
public int initUserApiCredentials();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||||
public int insertUser(SysUser user)
|
public int insertUser(SysUser user)
|
||||||
{
|
{
|
||||||
fillUserIdIfEmpty(user);
|
fillUserIdIfEmpty(user);
|
||||||
ensureApiCredentialsIfBlank(user);
|
|
||||||
// 新增用户信息
|
// 新增用户信息
|
||||||
int rows = userMapper.insertUser(user);
|
int rows = userMapper.insertUser(user);
|
||||||
// 新增用户岗位关联
|
// 新增用户岗位关联
|
||||||
|
|
@ -283,7 +282,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||||
public boolean registerUser(SysUser user)
|
public boolean registerUser(SysUser user)
|
||||||
{
|
{
|
||||||
fillUserIdIfEmpty(user);
|
fillUserIdIfEmpty(user);
|
||||||
ensureApiCredentialsIfBlank(user);
|
|
||||||
return userMapper.insertUser(user) > 0;
|
return userMapper.insertUser(user) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +296,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||||
public int updateUser(SysUser user)
|
public int updateUser(SysUser user)
|
||||||
{
|
{
|
||||||
String userId = user.getUserId();
|
String userId = user.getUserId();
|
||||||
handleApiCredentialsForUpdate(user);
|
|
||||||
// 删除用户与角色关联
|
// 删除用户与角色关联
|
||||||
userRoleMapper.deleteUserRoleByUserId(userId);
|
userRoleMapper.deleteUserRoleByUserId(userId);
|
||||||
// 新增用户与角色管理
|
// 新增用户与角色管理
|
||||||
|
|
@ -525,7 +522,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||||
user.setPassword(SecurityUtils.encryptPassword(password));
|
user.setPassword(SecurityUtils.encryptPassword(password));
|
||||||
user.setCreateBy(operName);
|
user.setCreateBy(operName);
|
||||||
fillUserIdIfEmpty(user);
|
fillUserIdIfEmpty(user);
|
||||||
ensureApiCredentialsIfBlank(user);
|
|
||||||
userMapper.insertUser(user);
|
userMapper.insertUser(user);
|
||||||
successNum++;
|
successNum++;
|
||||||
successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功");
|
successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功");
|
||||||
|
|
@ -569,24 +565,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||||
return successMsg.toString();
|
return successMsg.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public int initUserApiCredentials()
|
|
||||||
{
|
|
||||||
List<SysUser> users = userMapper.selectUsersWithoutApiCredentials();
|
|
||||||
if (CollectionUtils.isEmpty(users))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int count = 0;
|
|
||||||
for (SysUser user : users)
|
|
||||||
{
|
|
||||||
regenerateApiCredentials(user);
|
|
||||||
count += userMapper.updateApiCredentials(user.getUserId(), user.getApiKey(), user.getApiSecret());
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为空时生成用户主键,避免数据库默认值无法回填
|
* 为空时生成用户主键,避免数据库默认值无法回填
|
||||||
*/
|
*/
|
||||||
|
|
@ -597,44 +575,4 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||||
user.setUserId(IdUtils.randomUUID());
|
user.setUserId(IdUtils.randomUUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureApiCredentialsIfBlank(SysUser user)
|
|
||||||
{
|
|
||||||
if (StringUtils.isAnyBlank(user.getApiKey(), user.getApiSecret()))
|
|
||||||
{
|
|
||||||
regenerateApiCredentials(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleApiCredentialsForUpdate(SysUser user)
|
|
||||||
{
|
|
||||||
boolean apiKeyProvided = user.getApiKey() != null;
|
|
||||||
boolean apiSecretProvided = user.getApiSecret() != null;
|
|
||||||
|
|
||||||
// 前端显式传空,表示请求轮换密钥
|
|
||||||
if (apiKeyProvided || apiSecretProvided)
|
|
||||||
{
|
|
||||||
if (StringUtils.isAnyBlank(user.getApiKey(), user.getApiSecret()))
|
|
||||||
{
|
|
||||||
regenerateApiCredentials(user);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 未传密钥字段时,保持原值不变;仅当数据库原值为空时补生成
|
|
||||||
SysUser dbUser = userMapper.selectUserById(user.getUserId());
|
|
||||||
if (dbUser == null || StringUtils.isAnyBlank(dbUser.getApiKey(), dbUser.getApiSecret()))
|
|
||||||
{
|
|
||||||
regenerateApiCredentials(user);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
user.setApiKey(dbUser.getApiKey());
|
|
||||||
user.setApiSecret(dbUser.getApiSecret());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void regenerateApiCredentials(SysUser user)
|
|
||||||
{
|
|
||||||
user.setApiKey("ak_" + IdUtils.fastSimpleUUID());
|
|
||||||
user.setApiSecret("sk_" + IdUtils.fastSimpleUUID() + IdUtils.fastSimpleUUID());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
AND config_key like concat('%', #{configKey}, '%')
|
AND config_key like concat('%', #{configKey}, '%')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||||
and date(create_time) >= date(#{params.beginTime})
|
and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||||
and date(create_time) <= date(#{params.endTime})
|
and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -85,7 +85,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="configType != null and configType != ''">#{configType},</if>
|
<if test="configType != null and configType != ''">#{configType},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="configType != null and configType != ''">config_type = #{configType},</if>
|
<if test="configType != null and configType != ''">config_type = #{configType},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where config_id = #{configId}
|
where config_id = #{configId}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectChildrenDeptById" parameterType="String" resultMap="SysDeptResult">
|
<select id="selectChildrenDeptById" parameterType="String" resultMap="SysDeptResult">
|
||||||
select * from d_sys_dept where concat(',', ancestors, ',') like concat('%,', #{deptId}, ',%')
|
select * from d_sys_dept where find_in_set(#{deptId}, ancestors)
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectNormalChildrenDeptById" parameterType="String" resultType="int">
|
<select id="selectNormalChildrenDeptById" parameterType="String" resultType="int">
|
||||||
select count(*) from d_sys_dept where status = '0' and del_flag = '0' and concat(',', ancestors, ',') like concat('%,', #{deptId}, ',%')
|
select count(*) from d_sys_dept where status = 0 and del_flag = '0' and find_in_set(#{deptId}, ancestors)
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="checkDeptNameUnique" resultMap="SysDeptResult">
|
<select id="checkDeptNameUnique" resultMap="SysDeptResult">
|
||||||
|
|
@ -109,7 +109,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="email != null and email != ''">#{email},</if>
|
<if test="email != null and email != ''">#{email},</if>
|
||||||
<if test="status != null">#{status},</if>
|
<if test="status != null">#{status},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -125,7 +125,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="email != null">email = #{email},</if>
|
<if test="email != null">email = #{email},</if>
|
||||||
<if test="status != null and status != ''">status = #{status},</if>
|
<if test="status != null and status != ''">status = #{status},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where dept_id = #{deptId}
|
where dept_id = #{deptId}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null">status = #{status},</if>
|
<if test="status != null">status = #{status},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where dict_code = #{dictCode}
|
where dict_code = #{dictCode}
|
||||||
</update>
|
</update>
|
||||||
|
|
@ -117,7 +117,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null">#{status},</if>
|
<if test="status != null">#{status},</if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
AND dict_type like concat('%', #{dictType}, '%')
|
AND dict_type like concat('%', #{dictType}, '%')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||||
and date(create_time) >= date(#{params.beginTime})
|
and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||||
and date(create_time) <= date(#{params.endTime})
|
and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -79,7 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null">status = #{status},</if>
|
<if test="status != null">status = #{status},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where dict_id = #{dictId}
|
where dict_id = #{dictId}
|
||||||
</update>
|
</update>
|
||||||
|
|
@ -98,7 +98,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null">#{status},</if>
|
<if test="status != null">#{status},</if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
|
||||||
<insert id="insertLogininfor" parameterType="SysLogininfor">
|
<insert id="insertLogininfor" parameterType="SysLogininfor">
|
||||||
insert into d_sys_logininfor (user_name, status, ipaddr, msg, access_time)
|
insert into d_sys_logininfor (user_name, status, ipaddr, msg, access_time)
|
||||||
values (#{userName}, #{status}, #{ipaddr}, #{msg}, now())
|
values (#{userName}, #{status}, #{ipaddr}, #{msg}, sysdate())
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<select id="selectLogininforList" parameterType="SysLogininfor" resultMap="SysLogininforResult">
|
<select id="selectLogininforList" parameterType="SysLogininfor" resultMap="SysLogininforResult">
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectMenuVo">
|
<sql id="selectMenuVo">
|
||||||
select menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, coalesce(perms,'') as perms, icon, create_time
|
select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
|
||||||
from d_sys_menu
|
from d_sys_menu
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
|
|
@ -50,13 +50,13 @@
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectMenuTreeAll" resultMap="SysMenuResult">
|
<select id="selectMenuTreeAll" resultMap="SysMenuResult">
|
||||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, coalesce(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||||
from d_sys_menu m where m.menu_type in ('M', 'C') and m.status = '0'
|
from d_sys_menu m where m.menu_type in ('M', 'C') and m.status = 0
|
||||||
order by m.parent_id, m.order_num
|
order by m.parent_id, m.order_num
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
|
<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
|
||||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, coalesce(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||||
from d_sys_menu m
|
from d_sys_menu m
|
||||||
left join d_sys_role_menu rm on m.menu_id = rm.menu_id
|
left join d_sys_role_menu rm on m.menu_id = rm.menu_id
|
||||||
left join d_sys_user_role ur on rm.role_id = ur.role_id
|
left join d_sys_user_role ur on rm.role_id = ur.role_id
|
||||||
|
|
@ -75,13 +75,13 @@
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectMenuTreeByUserId" parameterType="String" resultMap="SysMenuResult">
|
<select id="selectMenuTreeByUserId" parameterType="String" resultMap="SysMenuResult">
|
||||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.route_name, m.visible, m.status, coalesce(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||||
from d_sys_menu m
|
from d_sys_menu m
|
||||||
left join d_sys_role_menu rm on m.menu_id = rm.menu_id
|
left join d_sys_role_menu rm on m.menu_id = rm.menu_id
|
||||||
left join d_sys_user_role ur on rm.role_id = ur.role_id
|
left join d_sys_user_role ur on rm.role_id = ur.role_id
|
||||||
left join d_sys_role ro on ur.role_id = ro.role_id
|
left join d_sys_role ro on ur.role_id = ro.role_id
|
||||||
left join d_sys_user u on ur.user_id = u.user_id
|
left join d_sys_user u on ur.user_id = u.user_id
|
||||||
where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = '0' AND ro.status = '0'
|
where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = 0 AND ro.status = 0
|
||||||
order by m.parent_id, m.order_num
|
order by m.parent_id, m.order_num
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
@ -141,7 +141,7 @@
|
||||||
<if test="orderNum != null">order_num = #{orderNum},</if>
|
<if test="orderNum != null">order_num = #{orderNum},</if>
|
||||||
<if test="path != null and path != ''">path = #{path},</if>
|
<if test="path != null and path != ''">path = #{path},</if>
|
||||||
<if test="component != null">component = #{component},</if>
|
<if test="component != null">component = #{component},</if>
|
||||||
<if test="query != null">query = #{query},</if>
|
<if test="query != null">`query` = #{query},</if>
|
||||||
<if test="routeName != null">route_name = #{routeName},</if>
|
<if test="routeName != null">route_name = #{routeName},</if>
|
||||||
<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
|
<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
|
||||||
<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
|
<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
<if test="icon !=null and icon != ''">icon = #{icon},</if>
|
<if test="icon !=null and icon != ''">icon = #{icon},</if>
|
||||||
<if test="remark != null and remark != ''">remark = #{remark},</if>
|
<if test="remark != null and remark != ''">remark = #{remark},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where menu_id = #{menuId}
|
where menu_id = #{menuId}
|
||||||
</update>
|
</update>
|
||||||
|
|
@ -165,7 +165,7 @@
|
||||||
<if test="orderNum != null">order_num,</if>
|
<if test="orderNum != null">order_num,</if>
|
||||||
<if test="path != null and path != ''">path,</if>
|
<if test="path != null and path != ''">path,</if>
|
||||||
<if test="component != null and component != ''">component,</if>
|
<if test="component != null and component != ''">component,</if>
|
||||||
<if test="query != null and query != ''">query,</if>
|
<if test="query != null and query != ''">`query`,</if>
|
||||||
<if test="routeName != null">route_name,</if>
|
<if test="routeName != null">route_name,</if>
|
||||||
<if test="isFrame != null and isFrame != ''">is_frame,</if>
|
<if test="isFrame != null and isFrame != ''">is_frame,</if>
|
||||||
<if test="isCache != null and isCache != ''">is_cache,</if>
|
<if test="isCache != null and isCache != ''">is_cache,</if>
|
||||||
|
|
@ -195,7 +195,7 @@
|
||||||
<if test="icon != null and icon != ''">#{icon},</if>
|
<if test="icon != null and icon != ''">#{icon},</if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null and status != ''">#{status}, </if>
|
<if test="status != null and status != ''">#{status}, </if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="noticeContent != null">notice_content = #{noticeContent}, </if>
|
<if test="noticeContent != null">notice_content = #{noticeContent}, </if>
|
||||||
<if test="status != null and status != ''">status = #{status}, </if>
|
<if test="status != null and status != ''">status = #{status}, </if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where notice_id = #{noticeId}
|
where notice_id = #{noticeId}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
|
||||||
<insert id="insertOperlog" parameterType="SysOperLog">
|
<insert id="insertOperlog" parameterType="SysOperLog">
|
||||||
insert into d_sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_param, json_result, status, error_msg, cost_time, oper_time)
|
insert into d_sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_param, json_result, status, error_msg, cost_time, oper_time)
|
||||||
values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, now())
|
values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate())
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<select id="selectOperLogList" parameterType="SysOperLog" resultMap="SysOperLogResult">
|
<select id="selectOperLogList" parameterType="SysOperLog" resultMap="SysOperLogResult">
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null and status != ''">status = #{status},</if>
|
<if test="status != null and status != ''">status = #{status},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where post_id = #{postId}
|
where post_id = #{postId}
|
||||||
</update>
|
</update>
|
||||||
|
|
@ -104,7 +104,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null and status != ''">#{status},</if>
|
<if test="status != null and status != ''">#{status},</if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
AND r.role_key like concat('%', #{roleKey}, '%')
|
AND r.role_key like concat('%', #{roleKey}, '%')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||||
and date(r.create_time) >= date(#{params.beginTime})
|
and date_format(r.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||||
and date(r.create_time) <= date(#{params.endTime})
|
and date_format(r.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
<!-- 数据范围过滤 -->
|
<!-- 数据范围过滤 -->
|
||||||
${params.dataScope}
|
${params.dataScope}
|
||||||
|
|
@ -117,7 +117,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null and status != ''">#{status},</if>
|
<if test="status != null and status != ''">#{status},</if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null and status != ''">status = #{status},</if>
|
<if test="status != null and status != ''">status = #{status},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where role_id = #{roleId}
|
where role_id = #{roleId}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<result property="sex" column="sex" />
|
<result property="sex" column="sex" />
|
||||||
<result property="avatar" column="avatar" />
|
<result property="avatar" column="avatar" />
|
||||||
<result property="password" column="password" />
|
<result property="password" column="password" />
|
||||||
<result property="apiKey" column="api_key" />
|
|
||||||
<result property="apiSecret" column="api_secret" />
|
|
||||||
<result property="status" column="status" />
|
<result property="status" column="status" />
|
||||||
<result property="delFlag" column="del_flag" />
|
<result property="delFlag" column="del_flag" />
|
||||||
<result property="loginIp" column="login_ip" />
|
<result property="loginIp" column="login_ip" />
|
||||||
|
|
@ -50,7 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectUserVo">
|
<sql id="selectUserVo">
|
||||||
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.api_key, u.api_secret, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.remark,
|
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.remark,
|
||||||
d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
|
d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
|
||||||
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
|
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
|
||||||
from d_sys_user u
|
from d_sys_user u
|
||||||
|
|
@ -60,7 +58,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
|
<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
|
||||||
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.api_key, u.api_secret, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from d_sys_user u
|
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from d_sys_user u
|
||||||
left join d_sys_dept d on u.dept_id = d.dept_id
|
left join d_sys_dept d on u.dept_id = d.dept_id
|
||||||
where u.del_flag = '0'
|
where u.del_flag = '0'
|
||||||
<if test="userId != null and userId != ''">
|
<if test="userId != null and userId != ''">
|
||||||
|
|
@ -76,13 +74,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
AND u.phonenumber like concat('%', #{phonenumber}, '%')
|
AND u.phonenumber like concat('%', #{phonenumber}, '%')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||||
AND date(u.create_time) >= date(#{params.beginTime})
|
AND date_format(u.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||||
AND date(u.create_time) <= date(#{params.endTime})
|
AND date_format(u.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
<if test="deptId != null and deptId != ''">
|
<if test="deptId != null and deptId != ''">
|
||||||
AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM d_sys_dept t WHERE concat(',', ancestors, ',') like concat('%,', #{deptId}, ',%') ))
|
AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM d_sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
|
||||||
</if>
|
</if>
|
||||||
<!-- 数据范围过滤 -->
|
<!-- 数据范围过滤 -->
|
||||||
${params.dataScope}
|
${params.dataScope}
|
||||||
|
|
@ -145,13 +143,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
select user_id, email from d_sys_user where email = #{email} and del_flag = '0' limit 1
|
select user_id, email from d_sys_user where email = #{email} and del_flag = '0' limit 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectUsersWithoutApiCredentials" resultMap="SysUserResult">
|
|
||||||
select user_id, user_name, api_key, api_secret
|
|
||||||
from d_sys_user
|
|
||||||
where del_flag = '0'
|
|
||||||
and (api_key is null or api_key = '' or api_secret is null or api_secret = '')
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
|
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
|
||||||
insert into d_sys_user(
|
insert into d_sys_user(
|
||||||
<if test="userId != null and userId != ''">user_id,</if>
|
<if test="userId != null and userId != ''">user_id,</if>
|
||||||
|
|
@ -163,8 +154,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
|
<if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
|
||||||
<if test="sex != null and sex != ''">sex,</if>
|
<if test="sex != null and sex != ''">sex,</if>
|
||||||
<if test="password != null and password != ''">password,</if>
|
<if test="password != null and password != ''">password,</if>
|
||||||
<if test="apiKey != null and apiKey != ''">api_key,</if>
|
|
||||||
<if test="apiSecret != null and apiSecret != ''">api_secret,</if>
|
|
||||||
<if test="status != null and status != ''">status,</if>
|
<if test="status != null and status != ''">status,</if>
|
||||||
<if test="pwdUpdateDate != null">pwd_update_date,</if>
|
<if test="pwdUpdateDate != null">pwd_update_date,</if>
|
||||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||||
|
|
@ -180,13 +169,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
|
<if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
|
||||||
<if test="sex != null and sex != ''">#{sex},</if>
|
<if test="sex != null and sex != ''">#{sex},</if>
|
||||||
<if test="password != null and password != ''">#{password},</if>
|
<if test="password != null and password != ''">#{password},</if>
|
||||||
<if test="apiKey != null and apiKey != ''">#{apiKey},</if>
|
|
||||||
<if test="apiSecret != null and apiSecret != ''">#{apiSecret},</if>
|
|
||||||
<if test="status != null and status != ''">#{status},</if>
|
<if test="status != null and status != ''">#{status},</if>
|
||||||
<if test="pwdUpdateDate != null">#{pwdUpdateDate},</if>
|
<if test="pwdUpdateDate != null">#{pwdUpdateDate},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
now()
|
sysdate()
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -200,24 +187,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="sex != null and sex != ''">sex = #{sex},</if>
|
<if test="sex != null and sex != ''">sex = #{sex},</if>
|
||||||
<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
|
<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
|
||||||
<if test="password != null and password != ''">password = #{password},</if>
|
<if test="password != null and password != ''">password = #{password},</if>
|
||||||
<if test="apiKey != null and apiKey != ''">api_key = #{apiKey},</if>
|
|
||||||
<if test="apiSecret != null and apiSecret != ''">api_secret = #{apiSecret},</if>
|
|
||||||
<if test="status != null and status != ''">status = #{status},</if>
|
<if test="status != null and status != ''">status = #{status},</if>
|
||||||
<if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
|
<if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
|
||||||
<if test="loginDate != null">login_date = #{loginDate},</if>
|
<if test="loginDate != null">login_date = #{loginDate},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
update_time = now()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
where user_id = #{userId}
|
where user_id = #{userId}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<update id="updateUserStatus" parameterType="SysUser">
|
<update id="updateUserStatus" parameterType="SysUser">
|
||||||
update d_sys_user set status = #{status}, update_time = now() where user_id = #{userId}
|
update d_sys_user set status = #{status}, update_time = sysdate() where user_id = #{userId}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<update id="updateUserAvatar" parameterType="SysUser">
|
<update id="updateUserAvatar" parameterType="SysUser">
|
||||||
update d_sys_user set avatar = #{avatar}, update_time = now() where user_id = #{userId}
|
update d_sys_user set avatar = #{avatar}, update_time = sysdate() where user_id = #{userId}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<update id="updateLoginInfo" parameterType="SysUser">
|
<update id="updateLoginInfo" parameterType="SysUser">
|
||||||
|
|
@ -225,15 +210,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<update id="resetUserPwd" parameterType="SysUser">
|
<update id="resetUserPwd" parameterType="SysUser">
|
||||||
update d_sys_user set pwd_update_date = now(), password = #{password}, update_time = now() where user_id = #{userId}
|
update d_sys_user set pwd_update_date = sysdate(), password = #{password}, update_time = sysdate() where user_id = #{userId}
|
||||||
</update>
|
|
||||||
|
|
||||||
<update id="updateApiCredentials">
|
|
||||||
update d_sys_user
|
|
||||||
set api_key = #{apiKey},
|
|
||||||
api_secret = #{apiSecret},
|
|
||||||
update_time = now()
|
|
||||||
where user_id = #{userId}
|
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<delete id="deleteUserById" parameterType="String">
|
<delete id="deleteUserById" parameterType="String">
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
<modules>
|
<modules>
|
||||||
<module>llm-guard-system</module>
|
<module>llm-guard-system</module>
|
||||||
<module>llm-guard-biz</module>
|
<module>llm-guard-biz</module>
|
||||||
<module>llm-guard-open-api</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<artifactId>llm-guard-modules</artifactId>
|
<artifactId>llm-guard-modules</artifactId>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue