JeecgBoot 3.6.0大版本发布

pull/5563/head
zhangdaiscott 2023-10-18 15:04:41 +08:00
parent f342c93eec
commit 524cd4a3d1
267 changed files with 12894 additions and 49368 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ rebel.xml
## front
**/*.lock
os_del.cmd

View File

@ -7,13 +7,13 @@
JEECG BOOT Low Code Development Platform
===============
The Latest Version 3.5.5Release date2023-09-22
当前最新版本: 3.6.0发布日期2023-10-23
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-guojusoft-orange.svg)](http://www.jeecg.com)
[![](https://img.shields.io/badge/Blog-blog-blue.svg)](https://jeecg.blog.csdn.net)
[![](https://img.shields.io/badge/version-3.5.5-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-3.6.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)

View File

@ -7,13 +7,13 @@
JEECG BOOT 低代码开发平台
===============
当前最新版本: 3.5.5发布日期2023-09-22
当前最新版本: 3.6.0发布日期2023-10-23
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://jeecg.com/aboutusIndex)
[![](https://img.shields.io/badge/Blog-官方博客-blue.svg)](https://jeecg.blog.csdn.net)
[![](https://img.shields.io/badge/version-3.5.5-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-3.6.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +0,0 @@
oracle导出编码 export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
导出用户: jeecgboot
导入命令: imp scott/tiger@orcl file=jeecgboot-oracle11g.dmp

View File

@ -1,49 +0,0 @@
-- sql
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1609123240547344385', '1280350452934307841', '', NULL, NULL, 0, NULL, NULL, 2, 'system:tenant:packList', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2022-12-31 17:44:11', NULL, NULL, 0, 0, '1', 0);
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1609123437247619074', '1280350452934307841', '', NULL, NULL, 0, NULL, NULL, 2, 'system:tenant:add:pack', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2022-12-31 17:44:58', 'admin', '2022-12-31 20:27:56', 0, 0, '1', 0);
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1609164542165012482', '1280350452934307841', '', NULL, NULL, 0, NULL, NULL, 2, 'system:tenant:edit:pack', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2022-12-31 20:28:18', NULL, NULL, 0, 0, '1', 0);
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1609164635442139138', '1280350452934307841', '', NULL, NULL, 0, NULL, NULL, 2, 'system:tenant:delete:pack', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2022-12-31 20:28:41', NULL, NULL, 0, 0, '1', 0);
--
ALTER TABLE sys_depart
MODIFY COLUMN iz_leaf tinyint(1) NULL DEFAULT 1 COMMENT ': 10' AFTER tenant_id;
-- ---
update sys_depart set iz_leaf = 0 where id in ( select a.parent_id from (select parent_id from sys_depart where parent_id!='' and parent_id is not null) as a);
update sys_depart set iz_leaf = 1 where id not in ( select a.parent_id from (select parent_id from sys_depart where parent_id!='' and parent_id is not null) as a);
--
-- vue2
UPDATE sys_permission_v2 SET is_leaf = 0 WHERE id = '58857ff846e61794c69208e9d3a85466';
INSERT INTO sys_permission_v2(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) VALUES ('1660568280725127169', '58857ff846e61794c69208e9d3a85466', '', NULL, NULL, NULL, NULL, 2, 'system:log:list', '1', NULL, 0, NULL, 1, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-05-22 16:48:25', NULL, NULL, 0);
INSERT INTO sys_permission_v2(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) VALUES ('1660568368558047234', '58857ff846e61794c69208e9d3a85466', '', NULL, NULL, NULL, NULL, 2, 'system:log:delete', '1', NULL, 0, NULL, 1, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-05-22 16:48:46', NULL, NULL, 0);
INSERT INTO sys_permission_v2(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) VALUES ('1660568426632380417', '58857ff846e61794c69208e9d3a85466', '', NULL, NULL, NULL, NULL, 2, 'system:log:deleteBatch', '1', NULL, 0, NULL, 1, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-05-22 16:48:59', NULL, NULL, 0);
-- vue3
UPDATE sys_permission SET is_leaf = 0 WHERE id = '1439533711676973057';
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) VALUES ('1660568280725127169', '1439533711676973057', '', NULL, NULL, NULL, NULL, 2, 'system:log:list', '1', NULL, 0, NULL, 1, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-05-22 16:48:25', NULL, NULL, 0);
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) VALUES ('1660568368558047234', '1439533711676973057', '', NULL, NULL, NULL, NULL, 2, 'system:log:delete', '1', NULL, 0, NULL, 1, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-05-22 16:48:46', NULL, NULL, 0);
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) VALUES ('1660568426632380417', '1439533711676973057', '', NULL, NULL, NULL, NULL, 2, 'system:log:deleteBatch', '1', NULL, 0, NULL, 1, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-05-22 16:48:59', NULL, NULL, 0);
-- ---
ALTER TABLE sys_data_log
MODIFY COLUMN `data_table` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '' AFTER `update_time`;
-- ---
ALTER TABLE `sys_announcement_send`
ADD INDEX `idx_sacm_annt_id`(`annt_id`),
ADD INDEX `idx_sacm_user_id`(`user_id`),
ADD INDEX `idx_sacm_read_flag`(`read_flag`),
ADD INDEX `idx_sacm_star_flag`(`star_flag`);
ALTER TABLE `sys_announcement`
ADD INDEX `idx_sanno_endtime`(`end_time`),
ADD INDEX `idx_sanno_start_time`(`start_time`),
ADD INDEX `idx_sanno_msg_type`(`msg_type`),
ADD INDEX `idx_sanno_send_status`(`send_status`),
ADD INDEX `idx_sanno_del_flag`(`del_flag`),
ADD INDEX `idx_sanno_tenant_id`(`tenant_id`),
ADD INDEX `idx_sanno_sender`(`sender`),
ADD INDEX `idx_sanno_create_time`(`create_time`);

View File

@ -0,0 +1,155 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
--
CREATE TABLE sys_user_position (
id varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '',
user_id varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'id',
position_id varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'id',
create_by varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '',
create_time datetime(0) NULL DEFAULT NULL COMMENT '',
update_by varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '',
update_time datetime(0) NULL DEFAULT NULL COMMENT '',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
ALTER TABLE sys_user_position
ADD INDEX idx_sup_user_id(user_id) USING BTREE,
ADD INDEX idx_sup_position_id(position_id) USING BTREE,
ADD INDEX idx_sup_user_position_id(user_id, position_id) USING BTREE;
--
ALTER TABLE sys_user DROP COLUMN post;
--
ALTER TABLE `sys_user_tenant`
DROP INDEX `uniq_sut_user_rel_tenant`,
ADD INDEX `idx_sut_user_rel_tenant`(`user_id`, `tenant_id`) USING BTREE;
ALTER TABLE `sys_user_depart`
DROP INDEX `idx_sud_user_dep_id`,
ADD UNIQUE INDEX `idx_sud_user_dep_id`(`user_id`, `dep_id`) USING BTREE;
--
CREATE TABLE sys_third_app_config (
id varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
tenant_id int(10) NOT NULL DEFAULT 0 COMMENT 'id',
agent_id varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '/id',
client_id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '/ id',
client_secret varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '/id',
third_type varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '(dingtalk wechat_enterprise )',
agent_app_secret varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Secret',
status int(1) NULL DEFAULT 1 COMMENT '(0-,1-)',
create_time datetime(0) NULL DEFAULT NULL COMMENT '',
update_time datetime(0) NULL DEFAULT NULL COMMENT '',
PRIMARY KEY (id) USING BTREE,
UNIQUE INDEX uniq_stac_third_type_tenant_id(tenant_id, third_type) USING BTREE,
INDEX idx_stac_tenant_id(tenant_id) USING BTREE,
INDEX idx_stac_third_type(third_type) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '' ROW_FORMAT = Dynamic;
ALTER TABLE sys_third_account
ADD UNIQUE INDEX uniq_sta_user_id_third_type(sys_user_id, third_type) USING BTREE,
ADD UNIQUE INDEX uniq_sta_third_user_id_third_type(third_user_id, third_type) USING BTREE,
ADD UNIQUE INDEX uniq_sta_third_user_uuid_third_type(third_user_uuid, third_type) USING BTREE;
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1629109281748291586', 'd7d6e2e4e2934f2c9385a623fd98c6f3', '', '/third/app', 'system/appconfig/ThirdAppConfigList', 1, '', NULL, 1, NULL, '0', 13.00, 0, 'ant-design:setting-outlined', 1, 0, 0, 0, NULL, 'admin', '2023-02-24 21:21:35', 'admin', '2023-02-24 21:51:05', 0, 0, NULL, 0);
ALTER TABLE sys_third_account
ADD COLUMN tenant_id int(10) NULL DEFAULT 0 COMMENT 'id' AFTER realname;
update sys_third_account set tenant_id = 0;
ALTER TABLE sys_third_account
DROP INDEX uniq_sta_third_user_id_third_type,
DROP INDEX uniq_sta_third_user_uuid_third_type,
DROP INDEX uniq_sta_user_id_third_type,
ADD UNIQUE INDEX uniq_sta_third_user_id_third_type(third_user_id, third_type, tenant_id) USING BTREE,
ADD UNIQUE INDEX uniq_sta_third_user_uuid_third_type(third_user_uuid, third_type, tenant_id) USING BTREE;
ALTER TABLE `sys_third_app_config`
DROP INDEX `uniq_stac_third_type_tenant_id`;
--
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1674708136602542082', '', '', '/mytenant', 'layouts/RouteView', 1, '', NULL, 0, NULL, '0', 4.20, 0, 'ant-design:user-outlined', 0, 0, 0, 0, NULL, 'admin', '2023-06-30 17:15:09', 'admin', '2023-06-30 18:35:40', 0, 0, NULL, 0);
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1663816667704500225', '1674708136602542082', '', '/tenant/MyTenantList', 'system/tenant/MyTenantList', 1, '', NULL, 1, NULL, '0', 1.00, 0, 'ant-design:user-outlined', 1, 0, 0, 0, NULL, 'admin', '2023-05-31 15:56:20', 'admin', '2023-06-30 18:37:26', 0, 0, NULL, 0);
UPDATE sys_permission SET parent_id = '1674708136602542082' WHERE component = 'system/user/TenantUserList';
UPDATE sys_permission SET parent_id = '1674708136602542082' WHERE component = 'system/role/TenantRoleList';
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1661572802889007106', '', '', '/tenant/setting', 'layouts/RouteView', 1, NULL, NULL, 0, NULL, '1', 4.10, 0, 'ant-design:setting-outlined', 0, 0, 0, 0, NULL, 'admin', '2023-05-25 11:20:01', 'admin', '2023-06-30 18:37:04', 0, 0, '1', 0);
UPDATE sys_permission SET parent_id = '1661572802889007106' WHERE component = 'system/tenant/index';
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1668174661456171010', '1661572802889007106', '', '/tenant/TenantDefaultPack', 'system/tenant/TenantDefaultPackList', 1, '', NULL, 1, NULL, '0', 5.00, 0, 'ant-design:folder-filled', 1, 0, 0, 0, NULL, 'admin', '2023-06-12 16:33:27', 'admin', '2023-06-30 19:09:24', 0, 0, NULL, 0);
ALTER TABLE sys_tenant_pack
ADD COLUMN pack_type varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'custom' COMMENT '(default custom )' AFTER pack_code;
update sys_tenant_pack set pack_type = 'custom';
ALTER TABLE sys_user_tenant
MODIFY COLUMN status varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '(1 2 3 4 5 )' AFTER tenant_id;
UPDATE sys_permission SET component = 'system/tenant/my/MyTenantList' WHERE component = 'system/tenant/MyTenantList';
UPDATE sys_permission SET component = 'system/tenant/pack/TenantDefaultPackList' WHERE component = 'system/tenant/TenantDefaultPackList';
UPDATE sys_permission SET component = 'system/tenant/TenantUserList', url='/system/tenant/TenantUserList' WHERE component = 'system/user/TenantUserList';
--
ALTER TABLE sys_announcement
MODIFY COLUMN bus_type varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '(email: bpm: tenant_invite:)' AFTER del_flag;
-- org_category
ALTER TABLE `sys_depart`
MODIFY COLUMN `org_category` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '1' COMMENT ' 123' AFTER `description`;
-- ---
ALTER TABLE `sys_data_log`
ADD COLUMN `create_name` varchar(100) NULL COMMENT '' AFTER `create_by`;
UPDATE sys_data_log
SET create_name = (SELECT sys_user.realname FROM sys_user WHERE sys_user.username = sys_data_log.create_by)
WHERE create_name = '' OR create_name IS NULL;
--
CREATE TABLE `sys_table_white_list` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'id',
`table_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '',
`field_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '',
`status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '1=0=',
`create_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '',
`create_time` datetime NULL DEFAULT NULL COMMENT '',
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '',
`update_time` datetime NULL DEFAULT NULL COMMENT '',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uniq_sys_table_white_list_table_name`(`table_name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '' ROW_FORMAT = DYNAMIC;
--
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701578033271521282', 'sys_user', 'id,realname,username', '1', 'admin', '2023-09-12 10:46:32', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701581935488385025', 'oa_officialdoc_organcode', 'id,organ_name', '1', 'admin', '2023-09-12 11:02:02', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701581977733414913', 'demo', 'id,name', '1', 'admin', '2023-09-12 11:02:12', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701582035472203777', 'sys_permission', 'id,name', '1', 'admin', '2023-09-12 11:02:26', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701582087619985409', 'onl_drag_comp', 'id,comp_name', '1', 'admin', '2023-09-12 11:02:38', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701582136420712450', 'sys_depart', 'id,depart_name', '1', 'admin', '2023-09-12 11:02:50', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701582163599802370', 'design_form', 'id,desform_name,desform_code', '1', 'admin', '2023-09-12 11:02:56', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701582190187495426', 'onl_cgform_head', 'table_txt,table_name', '1', 'admin', '2023-09-12 11:03:03', NULL, NULL);
INSERT INTO `sys_table_white_list` (`id`, `table_name`, `field_name`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1701582254301626370', 'oa_wps_file', 'id,name', '1', 'admin', '2023-09-12 11:03:18', NULL, NULL);
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1701575168519839746', 'd7d6e2e4e2934f2c9385a623fd98c6f3', '', '/system/tableWhiteList', 'system/tableWhiteList/SysTableWhiteListList', 1, '', NULL, 1, NULL, '0', 13.00, 0, 'ant-design:table-outlined', 1, 0, 0, 0, NULL, 'admin', '2023-09-12 20:35:09', 'admin', '2023-09-12 20:45:08', 0, 0, NULL, 0);
-- ---
ALTER TABLE `sys_announcement_send`
MODIFY COLUMN `read_flag` int(2) NULL DEFAULT NULL COMMENT '01' AFTER `user_id`;
ALTER TABLE `sys_announcement`
MODIFY COLUMN `msg_abstract` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '/' AFTER `user_ids`;
-- ---
ALTER TABLE sys_dict_item
ADD COLUMN item_color varchar(10) NULL COMMENT '' AFTER item_value;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -2,9 +2,9 @@
JeecgBoot属于平台级产品每次升级改动内容较多目前做不到平滑升级。
这里给用户的升级建议是这样的
1.代码升级 => 本地版本通过svn或者git做好主干在分支上做业务开发jeecg每次版本发布可以手工覆盖主干的代码对比代码进行提交
2.数据库升级 => 针对数据库我们每次发布会提供增量升级SQL可以通过增量SQL实现数据库的升级。
升级方案建议:
1.代码升级 => 本地版本通过svn或者git做好主干在分支上做业务开发jeecg每次版本发布可以手工覆盖主干的代码对比合并代码;
2.数据库升级 => 针对数据库我们每次发布会提供增量升级SQL可以通过执行增量SQL实现数据库的升级。
3.兼容问题 => 每次版本发布会针对不兼容地方标注说明,需要手工修改不兼容的代码。
注意: 升级sql目前只提供mysql版本执行完脚步后新菜单需要手工进行角色授权刷新首页才会出现。

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>3.5.5</version>
<version>3.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeecg-boot-base-core</artifactId>
@ -175,6 +175,10 @@
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
<exclusion>
<artifactId>checkstyle</artifactId>
<groupId>com.puppycrawl.tools</groupId>
</exclusion>
</exclusions>
</dependency>

View File

@ -102,12 +102,12 @@ public interface CommonAPI {
/**
* 13
* @param table
* @param tableFilterSql
* @param text
* @param code
* @return
*/
List<DictModel> queryTableDictItemsByCode(String table, String text, String code);
List<DictModel> queryTableDictItemsByCode(String tableFilterSql, String text, String code);
/**
* 14 dictCode

View File

@ -4,7 +4,7 @@ import lombok.Data;
import org.jeecg.common.constant.CommonConstant;
import java.io.Serializable;
import java.util.Map;
import java.util.*;
/**
*
@ -43,14 +43,7 @@ public class MessageDTO implements Serializable {
* 1: 2:
*/
protected String category;
//-----------------------------------------------------------------------
//update-begin---author:taoyan ---date:20220705 for支持自定义推送类型邮件、钉钉、企业微信、系统消息-----------
/**
*
*/
protected String templateCode;
/**
* org.jeecg.common.constant.enums.MessageTypeEnum
* XT("system", "系统消息")
@ -60,23 +53,38 @@ public class MessageDTO implements Serializable {
*/
protected String type;
//---【推送模板相关参数】-------------------------------------------------------------
/**
* Markdown
*/
protected boolean isMarkdown;
/**
*
*/
protected String templateCode;
/**
*
*/
protected Map<String, Object> data;
//update-end---author:taoyan ---date::20220705 for支持自定义推送类型邮件、钉钉、企业微信、系统消息-----------
//-----------------------------------------------------------------------
//---【推送模板相关参数】-------------------------------------------------------------
//---【邮件相关参数】-------------------------------------------------------------
/**
*
*
*/
private String copyToUser;
/**
*
*/
protected Set<String> toEmailList;
/**
*
*/
protected Set<String> ccEmailList;
//---【邮件相关参数】-------------------------------------------------------------
public MessageDTO(){
}

View File

@ -1,33 +0,0 @@
package org.jeecg.common.aspect.annotation;
import java.lang.annotation.*;
import org.jeecg.common.constant.enums.LowAppAopEnum;
/**
* low_app_id
*
* @Author scott
* @email jeecgos@163.com
* @Date 20220105
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoLowApp {
/**
* adddeletedb_import
*
* @return
*/
LowAppAopEnum action();
/**
* cgform
*
* @return
*/
String bizType();
}

View File

@ -112,8 +112,8 @@ public interface CommonConstant {
String HAS_CANCLE = "2";
/**阅读状态0未读1已读*/
String HAS_READ_FLAG = "1";
String NO_READ_FLAG = "0";
Integer HAS_READ_FLAG = 1;
Integer NO_READ_FLAG = 0;
/**优先级L低M中H高*/
String PRIORITY_L = "L";
@ -160,6 +160,8 @@ public interface CommonConstant {
/**字典翻译文本后缀*/
String DICT_TEXT_SUFFIX = "_dictText";
/**字典翻译颜色后缀*/
String DICT_COLOR_SUFFIX = "_dictColor";
/**
*
@ -315,6 +317,8 @@ public interface CommonConstant {
String X_TIMESTAMP = "X-TIMESTAMP";
/** 租户请求头 更名为X-Tenant-Id */
String TENANT_ID = "X-Tenant-Id";
/** 简流接口请求头,用于排除不支持的控件字段 */
String X_MiniFlowExclusionFieldMode = "X-Miniflowexclusionfieldmode";
/**===============================================================================================*/
String TOKEN_IS_INVALID_MSG = "Token失效请重新登录!";
@ -388,6 +392,7 @@ public interface CommonConstant {
/** 部门表唯一keyorgCode */
String DEPART_KEY_ORG_CODE = "orgCode";
/**======【消息推送相关】==============================================================================*/
/**
* map
*/
@ -398,6 +403,11 @@ public interface CommonConstant {
*/
String NOTICE_MSG_BUS_ID = "NOTICE_MSG_BUS_ID";
/**
*
*/
String NOTICE_MSG_BUS_TYPE = "NOTICE_MSG_BUS_TYPE";
/**
* token,token
*/
@ -420,6 +430,7 @@ public interface CommonConstant {
/** 消息模板markdown */
String MSG_TEMPLATE_TYPE_MD = "5";
/**========【消息推送相关】==========================================================================*/
/**
* redis-key
@ -481,6 +492,11 @@ public interface CommonConstant {
*/
String USER_TENANT_REFUSE = "4";
/**
* ()
*/
String USER_TENANT_INVITE = "5";
/**
*
*/
@ -490,4 +506,71 @@ public interface CommonConstant {
*
*/
Integer IS_LEAF = 1;
/**
*
*/
String DINGTALK = "DINGTALK";
/**
*
*/
String WECHAT_ENTERPRISE = "WECHAT_ENTERPRISE";
/**
* id 0
*/
Integer TENANT_ID_DEFAULT_VALUE = 0;
/**
* low-app
*/
String COPY_LEVEL_APP = "app";
/**
* low-app
*/
String COPY_LEVEL_MENU = "menu";
/**
* low-app
*/
String COPY_LEVEL_BAK = "backup";
/**
* low-app
*/
String COPY_LEVEL_COVER = "cover";
/** 【QQYUN-6034】关联字段变更历史值缓存半个小时 */
String CACHE_REL_FIELD_OLD_VAL = "sys:cache:desform:relFieldOldVal:";
/**
*
*/
String ORDER_TYPE_ASC = "ASC";
/**
*
*/
String ORDER_TYPE_DESC = "DESC";
//update-begin---author:scott ---date:2023-09-10 for积木报表常量----
/**
*
*/
public static String[] allowDevRoles = new String[]{"lowdeveloper", "admin"};
/**
*
*
*/
public static final String SAAS_MODE_CREATED = "created";
/**
*
*
*/
public static final String SAAS_MODE_TENANT = "tenant";
//update-end---author:scott ---date::2023-09-10 for积木报表常量----
}

View File

@ -28,12 +28,21 @@ public interface CommonSendStatus {
public static final String APP_SESSION_SUFFIX = "_app";
/**-----【流程相关通知模板code】------------------------------------------------------------*/
/**流程催办——系统通知消息模板*/
public static final String TZMB_BPM_CUIBAN = "bpm_cuiban";
/**流程抄送——系统通知消息模板*/
public static final String TZMB_BPM_CC = "bpm_cc";
/**流程催办——邮件通知消息模板*/
public static final String TZMB_BPM_CUIBAN_EMAIL = "bpm_cuiban_email";
/**标准模板—系统消息通知*/
public static final String TZMB_SYS_TS_NOTE = "sys_ts_note";
/**流程超时提醒——系统通知消息模板*/
public static final String TZMB_BPM_CHAOSHI_TIP = "bpm_chaoshi_tip";
/**-----【流程相关通知模板code】-----------------------------------------------------------*/
/**
*
*/
public static final String MSG_ABSTRACT_JSON = "msg_abstract";
}

View File

@ -116,4 +116,8 @@ public class SymbolConstant {
*/
public static final String SQUARE_BRACKETS_RIGHT = "]";
/**
* ;
*/
public static final String SEMICOLON = ";";
}

View File

@ -1,4 +1,4 @@
package org.jeecg.common.util;
package org.jeecg.common.constant.enums;
import org.apache.commons.lang3.StringUtils;
@ -17,7 +17,11 @@ public enum DySmsEnum {
/**会议通知*/
MEET_NOTICE_TEMPLATE_CODE("SMS_201480469","JEECG","username,title,minute,time"),
/**我的计划通知*/
PLAN_NOTICE_TEMPLATE_CODE("SMS_201470515","JEECG","username,title,time");
PLAN_NOTICE_TEMPLATE_CODE("SMS_201470515","JEECG","username,title,time"),
/**支付成功短信通知*/
PAY_SUCCESS_NOTICE_CODE("SMS_461735163","敲敲云","realname,money,endTime"),
/**会员到期通知提醒*/
VIP_EXPIRE_NOTICE_CODE("SMS_461885023","敲敲云","realname,endTime");
/**
*

View File

@ -0,0 +1,66 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* html
*
* @author: liusq
* @Date: 2023-10-13
*/
public enum EmailTemplateEnum {
/**
*
*/
BPM_CUIBAN_EMAIL("bpm_cuiban_email", "/templates/email/bpm_cuiban_email.ftl"),
/**
*
*/
BPM_NEW_TASK_EMAIL("bpm_new_task_email", "/templates/email/bpm_new_task_email.ftl"),
/**
*
*/
DESFORM_NEW_DATA_EMAIL("desform_new_data_email", "/templates/email/desform_new_data_email.ftl");
/**
*
*/
private String name;
/**
*
*/
private String url;
EmailTemplateEnum(String name, String url) {
this.name = name;
this.url = url;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public static EmailTemplateEnum getByName(String name) {
if (oConvertUtils.isEmpty(name)) {
return null;
}
for (EmailTemplateEnum val : values()) {
if (val.getName().equals(name)) {
return val;
}
}
return null;
}
}

View File

@ -6,7 +6,7 @@ import org.jeecg.common.util.oConvertUtils;
*
*/
public enum FileTypeEnum {
// 文档类型folder:文件夹 excel:excel doc:word pp:ppt image:图片 archive:其他文档 video:视频
// 文档类型folder:文件夹 excel:excel doc:word pp:ppt image:图片 archive:其他文档 video:视频 voice:语音
// FOLDER
xls(".xls","excel","excel"),
xlsx(".xlsx","excel","excel"),
@ -26,7 +26,8 @@ public enum FileTypeEnum {
flv(".flv","video","视频"),
mp4(".mp4","video","视频"),
zip(".zip","zip","压缩包"),
pdf(".pdf","pdf","pdf");
pdf(".pdf","pdf","pdf"),
mp3(".mp3","mp3","语音");
private String type;
private String value;

View File

@ -1,30 +0,0 @@
package org.jeecg.common.constant.enums;
/**
* LowApp
* @date 2022-1-5
* @author: jeecg-boot
*/
public enum LowAppAopEnum {
/**
*
*/
ADD,
/**
*
*/
DELETE,
/** 复制表单操作 */
COPY,
/**
* OnlineOnline
*/
CGFORM_DB_IMPORT,
/**
*
*/
DESFORM_SUB2WORK
}

View File

@ -1,4 +1,6 @@
package org.jeecg.common.util;
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
*
@ -12,7 +14,16 @@ public enum SysAnnmentTypeEnum {
/**
*
*/
BPM("bpm", "url", "/bpm/task/MyTaskList");
BPM("bpm", "url", "/bpm/task/MyTaskList"),
/**
*
*/
BPM_VIEW("bpm_cc", "url", "/bpm/task/MyTaskList"),
/**
*
*/
TENANT_INVITE("tenant_invite", "url", "/system/usersetting");
/**
* (email: bpm:)

View File

@ -1,4 +1,4 @@
package org.jeecg.modules.message.enums;
package org.jeecg.common.constant.enums;
import org.jeecg.common.system.annotation.EnumDict;
import org.jeecg.common.system.vo.DictModel;
@ -18,6 +18,16 @@ public enum Vue3MessageHrefEnum {
*
*/
BPM("bpm", "/task/myHandleTaskInfo"),
/**
*
*/
BPM_SYSTEM_MSG("bpm_msg_node", ""),
/**
*
*/
BPM_VIEW("bpm_cc", "/task/myHandleTaskInfo"),
/**
*

View File

@ -19,7 +19,6 @@ import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
@ -29,7 +28,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Description: Controller
@ -70,7 +68,7 @@ public class JeecgController<T, S extends IService<T>> {
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.CLASS, clazz);
//update-begin--Author:liusq Date:20210126 for图片导出报错ImageBasePath未设置--------------------
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title);
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title);
exportParams.setImageBasePath(jeecgBaseConfig.getPath().getUpload());
//update-end--Author:liusq Date:20210126 for图片导出报错ImageBasePath未设置----------------------
mv.addObject(NormalExcelConstants.PARAMS,exportParams);
@ -110,7 +108,7 @@ public class JeecgController<T, S extends IService<T>> {
IPage<T> pageList = service.page(page, queryWrapper);
List<T> exportList = pageList.getRecords();
Map<String, Object> map = new HashMap<>(5);
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title+i,jeecgBaseConfig.getPath().getUpload());
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title+i,jeecgBaseConfig.getPath().getUpload());
exportParams.setType(ExcelType.XSSF);
//map.put("title",exportParams);
//表格Title

View File

@ -20,6 +20,14 @@ public class QueryCondition implements Serializable {
private String dbType;
private String rule;
private String val;
public QueryCondition(String field, String type, String dbType, String rule, String val) {
this.field = field;
this.type = type;
this.dbType = dbType;
this.rule = rule;
this.val = val;
}
public String getField() {
return field;

View File

@ -19,11 +19,9 @@ import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JeecgDataAutorUtils;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.util.SqlConcatUtil;
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.*;
import org.springframework.util.NumberUtils;
import com.alibaba.fastjson.JSON;
@ -143,7 +141,7 @@ public class QueryGenerator {
}
Object value = PropertyUtils.getSimpleProperty(searchObj, name);
column = getTableFieldName(searchObj.getClass(), name);
column = ReflectHelper.getTableFieldName(searchObj.getClass(), name);
if(column==null){
//column为null只有一种情况 那就是 添加了注解@TableField(exist = false) 后续都不用处理了
continue;
@ -283,15 +281,9 @@ public class QueryGenerator {
// 将现有排序 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1,column2 desc"
// 修改为 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1 desc,column2 desc"
if (order.toUpperCase().indexOf(ORDER_TYPE_ASC)>=0) {
//queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
String columnStr = oConvertUtils.camelToUnderline(column);
String[] columnArray = columnStr.split(",");
queryWrapper.orderByAsc(Arrays.asList(columnArray));
queryWrapper.orderByAsc(SqlInjectionUtil.getSqlInjectSortFields(column.split(",")));
} else {
//queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
String columnStr = oConvertUtils.camelToUnderline(column);
String[] columnArray = columnStr.split(",");
queryWrapper.orderByDesc(Arrays.asList(columnArray));
queryWrapper.orderByDesc(SqlInjectionUtil.getSqlInjectSortFields(column.split(",")));
}
//update-end--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
}
@ -347,7 +339,7 @@ public class QueryGenerator {
return;
}
// update-end-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
log.info("---高级查询参数-->" + filterConditions);
log.debug("---高级查询参数-->" + filterConditions);
queryWrapper.and(andWrapper -> {
for (int i = 0; i < filterConditions.size(); i++) {
@ -641,11 +633,11 @@ public class QueryGenerator {
* @param value
*/
public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
if (value == null || rule == null || oConvertUtils.isEmpty(value)) {
if (name==null || value == null || rule == null || oConvertUtils.isEmpty(value)) {
return;
}
name = oConvertUtils.camelToUnderline(name);
log.info("---查询过滤器Query规则---field:{}, rule:{}, value:{}",name,rule.getValue(),value);
log.debug("---高级查询 Query规则---field:{} , rule:{} , value:{}",name,rule.getValue(),value);
switch (rule) {
case GT:
queryWrapper.gt(name, value);
@ -713,7 +705,14 @@ public class QueryGenerator {
*/
public static Map<String, SysPermissionDataRuleModel> getRuleMap() {
Map<String, SysPermissionDataRuleModel> ruleMap = new HashMap<>(5);
List<SysPermissionDataRuleModel> list =JeecgDataAutorUtils.loadDataSearchConditon();
List<SysPermissionDataRuleModel> list = null;
//update-begin-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
try {
list = JeecgDataAutorUtils.loadDataSearchConditon();
}catch (Exception e){
log.error("根据request对象获取权限数据失败可能是定时任务中执行的。", e);
}
//update-end-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
if(list != null&&list.size()>0){
if(list.get(0)==null){
return ruleMap;
@ -821,223 +820,7 @@ public class QueryGenerator {
* @return
*/
public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString) {
return getSingleQueryConditionSql(field, alias, value, isString,null);
}
/**
*
* @param field
* @param alias
* @param value
* @param isString
* @param dataBaseType
* @return
*/
public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString, String dataBaseType) {
if (value == null) {
return "";
}
field = alias+oConvertUtils.camelToUnderline(field);
QueryRuleEnum rule = QueryGenerator.convert2Rule(value);
return getSingleSqlByRule(rule, field, value, isString, dataBaseType);
}
/**
*
* @param rule
* @param field
* @param value
* @param isString
* @param dataBaseType
* @return
*/
private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString, String dataBaseType) {
String res = "";
switch (rule) {
case GT:
res =field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case GE:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case LT:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case LE:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case EQ:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case EQ_WITH_ADD:
res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
break;
case NE:
res = field+" <> "+getFieldConditionValue(value, isString, dataBaseType);
break;
case IN:
res = field + " in "+getInConditionValue(value, isString);
break;
case LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LIKE);
break;
case LEFT_LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LEFT_LIKE);
break;
case RIGHT_LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.RIGHT_LIKE);
break;
default:
res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
break;
}
return res;
}
/**
*
* @param rule
* @param field
* @param value
* @param isString
* @return
*/
private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString) {
return getSingleSqlByRule(rule, field, value, isString, null);
}
/**
*
* @param value
* @param isString
* @param dataBaseType
* @return
*/
private static String getFieldConditionValue(Object value,boolean isString, String dataBaseType) {
String str = value.toString().trim();
if(str.startsWith(SymbolConstant.EXCLAMATORY_MARK)) {
str = str.substring(1);
}else if(str.startsWith(QueryRuleEnum.GE.getValue())) {
str = str.substring(2);
}else if(str.startsWith(QueryRuleEnum.LE.getValue())) {
str = str.substring(2);
}else if(str.startsWith(QueryRuleEnum.GT.getValue())) {
str = str.substring(1);
}else if(str.startsWith(QueryRuleEnum.LT.getValue())) {
str = str.substring(1);
}else if(str.indexOf(QUERY_COMMA_ESCAPE)>0) {
str = str.replaceAll("\\+\\+", COMMA);
}
if(dataBaseType==null){
dataBaseType = getDbType();
}
if(isString) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType)){
return " N'"+str+"' ";
}else{
return " '"+str+"' ";
}
}else {
// 如果不是字符串 有一种特殊情况 popup调用都走这个逻辑 参数传递的可能是“admin”这种格式的
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return " N"+str;
}
return value.toString();
}
}
private static String getInConditionValue(Object value,boolean isString) {
//update-begin-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
String[] temp = value.toString().split(",");
if(temp.length==0){
return "('')";
}
if(isString) {
List<String> res = new ArrayList<>();
for (String string : temp) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
res.add("N'"+string+"'");
}else{
res.add("'"+string+"'");
}
}
return "("+String.join("," ,res)+")";
}else {
return "("+value.toString()+")";
}
//update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
}
/**
*
* (*%)ruleEnum
* @param value
* @param ruleEnum
* @return
*/
private static String getLikeConditionValue(Object value, QueryRuleEnum ruleEnum) {
String str = value.toString().trim();
if(str.startsWith(SymbolConstant.ASTERISK) && str.endsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'%"+str.substring(1,str.length()-1)+"%'";
}else{
return "'%"+str.substring(1,str.length()-1)+"%'";
}
}else if(str.startsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'%"+str.substring(1)+"'";
}else{
return "'%"+str.substring(1)+"'";
}
}else if(str.endsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'"+str.substring(0,str.length()-1)+"%'";
}else{
return "'"+str.substring(0,str.length()-1)+"%'";
}
}else {
if(str.indexOf(SymbolConstant.PERCENT_SIGN)>=0) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return "N"+str;
}else{
return "N"+"'"+str+"'";
}
}else{
if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return str;
}else{
return "'"+str+"'";
}
}
}else {
//update-begin-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
// 走到这里说明 value不带有任何模糊查询的标识(*或者%)
if (ruleEnum == QueryRuleEnum.LEFT_LIKE) {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'%" + str + "'";
} else {
return "'%" + str + "'";
}
} else if (ruleEnum == QueryRuleEnum.RIGHT_LIKE) {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'" + str + "%'";
} else {
return "'" + str + "%'";
}
} else {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'%" + str + "%'";
} else {
return "'%" + str + "%'";
}
}
//update-end-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
}
}
return SqlConcatUtil.getSingleQueryConditionSql(field, alias, value, isString,null);
}
/**
@ -1064,7 +847,7 @@ public class QueryGenerator {
continue;
}
if(ruleMap.containsKey(name)) {
column = getTableFieldName(clazz, name);
column = ReflectHelper.getTableFieldName(clazz, name);
if(column==null){
continue;
}
@ -1078,7 +861,7 @@ public class QueryGenerator {
}else {
value = NumberUtils.parseNumber(dataRule.getRuleValue(),propType);
}
String filedSql = getSingleSqlByRule(rule, oConvertUtils.camelToUnderline(column), value,isString);
String filedSql = SqlConcatUtil.getSingleSqlByRule(rule, oConvertUtils.camelToUnderline(column), value,isString);
sb.append(sqlAnd+filedSql);
}
}
@ -1107,7 +890,7 @@ public class QueryGenerator {
if (judgedIsUselessField(name)) {
continue;
}
column = getTableFieldName(clazz, name);
column = ReflectHelper.getTableFieldName(clazz, name);
if(column==null){
continue;
}
@ -1126,42 +909,6 @@ public class QueryGenerator {
return getSqlRuleValue(sql);
}
/**
* sql
* @return
*/
public static String getAllConfigAuth() {
StringBuffer sb = new StringBuffer();
//权限查询
Map<String,SysPermissionDataRuleModel> ruleMap = getRuleMap();
String sqlAnd = " and ";
for (String c : ruleMap.keySet()) {
SysPermissionDataRuleModel dataRule = ruleMap.get(c);
String ruleValue = dataRule.getRuleValue();
if(oConvertUtils.isEmpty(ruleValue)){
continue;
}
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
sb.append(sqlAnd+getSqlRuleValue(ruleValue));
}else{
boolean isString = false;
ruleValue = ruleValue.trim();
if(ruleValue.startsWith("'") && ruleValue.endsWith("'")){
isString = true;
ruleValue = ruleValue.substring(1,ruleValue.length()-1);
}
QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
String value = converRuleValue(ruleValue);
String filedSql = getSingleSqlByRule(rule, c, value,isString);
sb.append(sqlAnd+filedSql);
}
}
log.info("query auth sql is = "+sb.toString());
return sb.toString();
}
/**
*
*/
@ -1169,71 +916,6 @@ public class QueryGenerator {
return CommonUtils.getDatabaseType();
}
/**
* class
* @param clazz
* @return
*/
private static List<Field> getClassFields(Class<?> clazz) {
List<Field> list = new ArrayList<Field>();
Field[] fields;
do{
fields = clazz.getDeclaredFields();
for(int i = 0;i<fields.length;i++){
list.add(fields[i]);
}
clazz = clazz.getSuperclass();
}while(clazz!= Object.class&&clazz!=null);
return list;
}
/**
*
* @param clazz
* @param name
* @return
*/
private static String getTableFieldName(Class<?> clazz, String name) {
try {
//如果字段加注解了@TableField(exist = false),不走DB查询
Field field = null;
try {
field = clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
//e.printStackTrace();
}
//如果为空,则去父类查找字段
if (field == null) {
List<Field> allFields = getClassFields(clazz);
List<Field> searchFields = allFields.stream().filter(a -> a.getName().equals(name)).collect(Collectors.toList());
if(searchFields!=null && searchFields.size()>0){
field = searchFields.get(0);
}
}
if (field != null) {
TableField tableField = field.getAnnotation(TableField.class);
if (tableField != null){
if(tableField.exist() == false){
//如果设置了TableField false 这个字段不需要处理
return null;
}else{
String column = tableField.value();
//如果设置了TableField value 这个字段是实体字段
if(!"".equals(column)){
return column;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
/**
* mysql 线 _\
*

View File

@ -25,12 +25,19 @@ public enum QueryRuleEnum {
IN("IN","in","包含"),
/**查询规则 全模糊*/
LIKE("LIKE","like","全模糊"),
/**查询规则 不模糊包含*/
NOT_LIKE("NOT_LIKE","not_like","不模糊包含"),
/**查询规则 左模糊*/
LEFT_LIKE("LEFT_LIKE","left_like","左模糊"),
/**查询规则 右模糊*/
RIGHT_LIKE("RIGHT_LIKE","right_like","右模糊"),
/**查询规则 带加号等于*/
EQ_WITH_ADD("EQWITHADD","eq_with_add","带加号等于"),
/**查询规则 多词模糊匹配*/
LIKE_WITH_AND("LIKEWITHAND","like_with_and","多词模糊匹配————暂时未用上"),
/**查询规则 自定义SQL片段*/
SQL_RULES("USE_SQL_RULES","ext","自定义SQL片段"),
// ------- 当前表单设计器内专用 -------
/** 值为空 */
EMPTY("EMPTY","empty","值为空"),
@ -38,15 +45,12 @@ public enum QueryRuleEnum {
NOT_EMPTY("NOT_EMPTY","not_empty","值不为空"),
/**查询规则 不包含*/
NOT_IN("NOT_IN","not_in","不包含"),
// ------- 当前表单设计器内专用 -------
/**查询规则 多词模糊匹配*/
LIKE_WITH_AND("LIKEWITHAND","like_with_and","多词模糊匹配————暂时未用上"),
/**查询规则 自定义SQL片段*/
SQL_RULES("USE_SQL_RULES","ext","自定义SQL片段"),
/**查询规则 多词匹配*/
ELE_MATCH("ELE_MATCH","elemMatch","多词匹配"),
/**查询规则 范围查询*/
RANGE("RANGE","range","范围查询");
RANGE("RANGE","range","范围查询"),
NOT_RANGE("NOT_RANGE","not_range","不在范围查询");
// ------- 当前表单设计器内专用 -------
private String value;
@ -89,7 +93,7 @@ public enum QueryRuleEnum {
return null;
}
for(QueryRuleEnum val :values()){
if (val.getValue().equals(value) || val.getCondition().equals(value)){
if (val.getValue().equals(value) || val.getCondition().equalsIgnoreCase(value)){
return val;
}
}

View File

@ -16,6 +16,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
@ -34,6 +35,7 @@ import org.jeecg.common.util.oConvertUtils;
* @Date 2018-07-12 14:23
* @Desc JWT
**/
@Slf4j
public class JwtUtil {
/**Token有效期为7天Token在reids中缓存时间为两倍*/
@ -163,15 +165,24 @@ public class JwtUtil {
* @param user
* @return
*/
public static String getUserSystemData(String key,SysUserCacheInfo user) {
public static String getUserSystemData(String key, SysUserCacheInfo user) {
//1.优先获取 SysUserCacheInfo
if(user==null) {
user = JeecgDataAutorUtils.loadUserInfo();
try {
user = JeecgDataAutorUtils.loadUserInfo();
} catch (Exception e) {
log.warn("获取用户信息异常:" + e.getMessage());
}
}
//2.通过shiro获取登录用户信息
LoginUser sysUser = null;
try {
sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
} catch (Exception e) {
log.warn("SecurityUtils.getSubject() 获取用户信息异常:" + e.getMessage());
}
//#{sys_user_code}%
// 获取登录用户信息
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String moshi = "";
String wellNumber = WELL_NUMBER;
if(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){
@ -184,6 +195,24 @@ public class JwtUtil {
} else {
key = key;
}
//替换为当前系统时间(年月日)
if (key.equals(DataBaseConstant.SYS_DATE)|| key.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) {
returnValue = DateUtils.formatDate();
}
//替换为当前系统时间(年月日时分秒)
else if (key.equals(DataBaseConstant.SYS_TIME)|| key.toLowerCase().equals(DataBaseConstant.SYS_TIME_TABLE)) {
returnValue = DateUtils.now();
}
//流程状态默认值(默认未发起)
else if (key.equals(DataBaseConstant.BPM_STATUS)|| key.toLowerCase().equals(DataBaseConstant.BPM_STATUS_TABLE)) {
returnValue = "1";
}
//后台任务获取用户信息异常,导致程序中断
if(sysUser==null && user==null){
return null;
}
//替换为系统登录用户帐号
if (key.equals(DataBaseConstant.SYS_USER_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_USER_CODE_TABLE)) {
if(user==null) {
@ -222,21 +251,13 @@ public class JwtUtil {
}
}
}
//替换为当前系统时间(年月日)
else if (key.equals(DataBaseConstant.SYS_DATE)|| key.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) {
returnValue = DateUtils.formatDate();
}
//替换为当前系统时间(年月日时分秒)
else if (key.equals(DataBaseConstant.SYS_TIME)|| key.toLowerCase().equals(DataBaseConstant.SYS_TIME_TABLE)) {
returnValue = DateUtils.now();
}
//流程状态默认值(默认未发起)
else if (key.equals(DataBaseConstant.BPM_STATUS)|| key.toLowerCase().equals(DataBaseConstant.BPM_STATUS_TABLE)) {
returnValue = "1";
}
//update-begin-author:taoyan date:20210330 for:多租户ID作为系统变量
else if (key.equals(TenantConstant.TENANT_ID) || key.toLowerCase().equals(TenantConstant.TENANT_ID_TABLE)){
returnValue = SpringContextUtils.getHttpServletRequest().getHeader(CommonConstant.TENANT_ID);
try {
returnValue = SpringContextUtils.getHttpServletRequest().getHeader(CommonConstant.TENANT_ID);
} catch (Exception e) {
log.warn("获取系统租户异常:" + e.getMessage());
}
}
//update-end-author:taoyan date:20210330 for:多租户ID作为系统变量
if(returnValue!=null){returnValue = returnValue + moshi;}

View File

@ -0,0 +1,243 @@
package org.jeecg.common.system.util;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.oConvertUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: SQL
* @author:qinfeng
* @date 20230904
*/
@Slf4j
public class SqlConcatUtil {
/**
*
* @param rule
* @param field
* @param value
* @param isString
* @return
*/
public static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString) {
return getSingleSqlByRule(rule, field, value, isString, null);
}
/**
*
* @param field
* @param alias
* @param value
* @param isString
* @param dataBaseType
* @return
*/
public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString, String dataBaseType) {
if (value == null) {
return "";
}
field = alias+oConvertUtils.camelToUnderline(field);
QueryRuleEnum rule = QueryGenerator.convert2Rule(value);
return getSingleSqlByRule(rule, field, value, isString, dataBaseType);
}
/**
*
* @param rule
* @param field
* @param value
* @param isString
* @param dataBaseType
* @return
*/
private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString, String dataBaseType) {
String res = "";
switch (rule) {
case GT:
res =field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case GE:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case LT:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case LE:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case EQ:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case EQ_WITH_ADD:
res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
break;
case NE:
res = field+" <> "+getFieldConditionValue(value, isString, dataBaseType);
break;
case IN:
res = field + " in "+getInConditionValue(value, isString);
break;
case LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LIKE);
break;
case LEFT_LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LEFT_LIKE);
break;
case RIGHT_LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.RIGHT_LIKE);
break;
default:
res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
break;
}
return res;
}
/**
*
* @param value
* @param isString
* @param dataBaseType
* @return
*/
private static String getFieldConditionValue(Object value,boolean isString, String dataBaseType) {
String str = value.toString().trim();
if(str.startsWith(SymbolConstant.EXCLAMATORY_MARK)) {
str = str.substring(1);
}else if(str.startsWith(QueryRuleEnum.GE.getValue())) {
str = str.substring(2);
}else if(str.startsWith(QueryRuleEnum.LE.getValue())) {
str = str.substring(2);
}else if(str.startsWith(QueryRuleEnum.GT.getValue())) {
str = str.substring(1);
}else if(str.startsWith(QueryRuleEnum.LT.getValue())) {
str = str.substring(1);
}else if(str.indexOf(QueryGenerator.QUERY_COMMA_ESCAPE)>0) {
str = str.replaceAll("\\+\\+", SymbolConstant.COMMA);
}
if(dataBaseType==null){
dataBaseType = getDbType();
}
if(isString) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType)){
return " N'"+str+"' ";
}else{
return " '"+str+"' ";
}
}else {
// 如果不是字符串 有一种特殊情况 popup调用都走这个逻辑 参数传递的可能是“admin”这种格式的
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return " N"+str;
}
return value.toString();
}
}
private static String getInConditionValue(Object value,boolean isString) {
//update-begin-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
String[] temp = value.toString().split(",");
if(temp.length==0){
return "('')";
}
if(isString) {
List<String> res = new ArrayList<>();
for (String string : temp) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
res.add("N'"+string+"'");
}else{
res.add("'"+string+"'");
}
}
return "("+String.join("," ,res)+")";
}else {
return "("+value.toString()+")";
}
//update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
}
/**
*
* (*%)ruleEnum
* @param value
* @param ruleEnum
* @return
*/
private static String getLikeConditionValue(Object value, QueryRuleEnum ruleEnum) {
String str = value.toString().trim();
if(str.startsWith(SymbolConstant.ASTERISK) && str.endsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'%"+str.substring(1,str.length()-1)+"%'";
}else{
return "'%"+str.substring(1,str.length()-1)+"%'";
}
}else if(str.startsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'%"+str.substring(1)+"'";
}else{
return "'%"+str.substring(1)+"'";
}
}else if(str.endsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'"+str.substring(0,str.length()-1)+"%'";
}else{
return "'"+str.substring(0,str.length()-1)+"%'";
}
}else {
if(str.indexOf(SymbolConstant.PERCENT_SIGN)>=0) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return "N"+str;
}else{
return "N"+"'"+str+"'";
}
}else{
if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return str;
}else{
return "'"+str+"'";
}
}
}else {
//update-begin-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
// 走到这里说明 value不带有任何模糊查询的标识(*或者%)
if (ruleEnum == QueryRuleEnum.LEFT_LIKE) {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'%" + str + "'";
} else {
return "'%" + str + "'";
}
} else if (ruleEnum == QueryRuleEnum.RIGHT_LIKE) {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'" + str + "%'";
} else {
return "'" + str + "%'";
}
} else {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'%" + str + "%'";
} else {
return "'%" + str + "%'";
}
}
//update-end-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
}
}
}
/**
*
*/
private static String getDbType() {
return CommonUtils.getDatabaseType();
}
}

View File

@ -2,6 +2,7 @@ package org.jeecg.common.system.vo;
import java.io.Serializable;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
@ -26,7 +27,13 @@ public class DictModel implements Serializable{
this.value = value;
this.text = text;
}
public DictModel(String value, String text, String color) {
this.value = value;
this.text = text;
this.color = color;
}
/**
* value
*/
@ -35,6 +42,10 @@ public class DictModel implements Serializable{
*
*/
private String text;
/**
*
*/
private String color;
/**
* JgEditableTable
@ -50,4 +61,11 @@ public class DictModel implements Serializable{
return this.text;
}
/**
*
* QQYUN-5595
*/
private JSONObject jsonObject;
}

View File

@ -1,15 +1,13 @@
package org.jeecg.common.system.vo;
import java.util.Date;
import org.jeecg.common.desensitization.annotation.SensitiveField;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.desensitization.annotation.SensitiveField;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* <p>
@ -51,6 +49,7 @@ public class LoginUser {
/**
* code
*/
@SensitiveField
private String orgCode;
/**
*
@ -61,7 +60,6 @@ public class LoginUser {
/**
*
*/
@SensitiveField
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@ -107,6 +105,7 @@ public class LoginUser {
/**
* ids
*/
@SensitiveField
private String departIds;
/**
@ -122,6 +121,7 @@ public class LoginUser {
private String telephone;
/** 多租户ids临时用不持久化数据库(数据库字段不存在) */
@SensitiveField
private String relTenantIds;
/**设备id uniapp推送用*/

View File

@ -0,0 +1,61 @@
package org.jeecg.common.system.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.desensitization.annotation.SensitiveField;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* <p>
* 线
* </p>
*
* @Author scott
* @since 2023-08-16
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class UserAccountInfo {
/**
* id
*/
private String id;
/**
*
*/
private String username;
/**
*
*/
private String realname;
/**
*
*/
private String email;
/**
*
*/
@SensitiveField
private String avatar;
/**
* 10
*/
private Integer activitiSync;
/**
*
*/
@SensitiveField
private String phone;
}

View File

@ -11,7 +11,8 @@ import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.ServiceNameConstants;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.filter.FileTypeFilter;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oss.OssBootUtil;
import org.jeecgframework.poi.util.PoiPublicUtil;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
@ -27,8 +28,7 @@ import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -138,6 +138,7 @@ public class CommonUtils {
}
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new JeecgBootException(e.getMessage());
}
return url;
}
@ -147,10 +148,10 @@ public class CommonUtils {
* @param bizPath
* @return
*/
public static String uploadLocal(MultipartFile mf, String bizPath, String uploadpath){
public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){
try {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
FileTypeFilter.fileTypeFilter(mf);
SsrfFileTypeFilter.checkUploadFileType(mf);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String fileName = null;
File file = new File(uploadpath + File.separator + bizPath + File.separator );
@ -274,7 +275,7 @@ public class CommonUtils {
if(db==null){
return null;
}
DriverManagerDataSource ds = new DriverManagerDataSource();
DriverManagerDataSource ds = new DriverManagerDataSource ();
ds.setDriverClassName(db.getDriverClassName());
ds.setUrl(db.getUrl());
ds.setUsername(db.getUsername());
@ -414,6 +415,10 @@ public class CommonUtils {
* @return name = '1212'
*/
public static String getFilterSqlByTableSql(String tableSql) {
if(oConvertUtils.isEmpty(tableSql)){
return null;
}
if (tableSql.toLowerCase().indexOf(DataBaseConstant.SQL_WHERE) > 0) {
String[] arr = tableSql.split(" (?i)where ");
if (arr != null && oConvertUtils.isNotEmpty(arr[1])) {
@ -430,6 +435,10 @@ public class CommonUtils {
* @return sys_user
*/
public static String getTableNameByTableSql(String tableSql) {
if(oConvertUtils.isEmpty(tableSql)){
return null;
}
if (tableSql.toLowerCase().indexOf(DataBaseConstant.SQL_WHERE) > 0) {
String[] arr = tableSql.split(" (?i)where ");
return arr[0].trim();
@ -437,4 +446,25 @@ public class CommonUtils {
return tableSql;
}
}
/**
*
* @param set1
* @param arr2
* @return
*/
public static boolean hasIntersection(Set<String> set1, String[] arr2) {
if (set1 == null) {
return false;
}
if(set1.size()>0){
for (String str : arr2) {
if (set1.contains(str)) {
return true;
}
}
}
return false;
}
}

View File

@ -1,9 +1,5 @@
package org.jeecg.common.util;
import org.jeecg.config.StaticConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
@ -12,6 +8,10 @@ import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.jeecg.common.constant.enums.DySmsEnum;
import org.jeecg.config.StaticConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created on 17/6/7.
@ -55,7 +55,7 @@ public class DySmsHelper {
}
public static boolean sendSms(String phone,JSONObject templateParamJson,DySmsEnum dySmsEnum) throws ClientException {
public static boolean sendSms(String phone, JSONObject templateParamJson, DySmsEnum dySmsEnum) throws ClientException {
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");

View File

@ -4,7 +4,7 @@ import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.filter.FileTypeFilter;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.filter.StrAttackFilter;
import org.springframework.web.multipart.MultipartFile;
@ -60,7 +60,7 @@ public class MinioUtil {
//update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
FileTypeFilter.fileTypeFilter(file);
SsrfFileTypeFilter.checkUploadFileType(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String newBucket = bucketName;

View File

@ -1,5 +1,6 @@
package org.jeecg.common.util;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
@ -7,6 +8,7 @@ import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @author
@ -252,4 +254,86 @@ public class ReflectHelper {
return value;
}
/**
*
* @param field
* @param clazz
* @return
*/
public static boolean isClassField(String field, Class clazz){
Field[] fields = clazz.getDeclaredFields();
for(int i=0;i<fields.length;i++){
String fieldName = fields[i].getName();
String tableColumnName = oConvertUtils.camelToUnderline(fieldName);
if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
return true;
}
}
return false;
}
/**
* class
* @param clazz
* @return
*/
public static List<Field> getClassFields(Class<?> clazz) {
List<Field> list = new ArrayList<Field>();
Field[] fields;
do{
fields = clazz.getDeclaredFields();
for(int i = 0;i<fields.length;i++){
list.add(fields[i]);
}
clazz = clazz.getSuperclass();
}while(clazz!= Object.class&&clazz!=null);
return list;
}
/**
*
* @param clazz
* @param name
* @return
*/
public static String getTableFieldName(Class<?> clazz, String name) {
try {
//如果字段加注解了@TableField(exist = false),不走DB查询
Field field = null;
try {
field = clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
//e.printStackTrace();
}
//如果为空,则去父类查找字段
if (field == null) {
List<Field> allFields = getClassFields(clazz);
List<Field> searchFields = allFields.stream().filter(a -> a.getName().equals(name)).collect(Collectors.toList());
if(searchFields!=null && searchFields.size()>0){
field = searchFields.get(0);
}
}
if (field != null) {
TableField tableField = field.getAnnotation(TableField.class);
if (tableField != null){
if(tableField.exist() == false){
//如果设置了TableField false 这个字段不需要处理
return null;
}else{
String column = tableField.value();
//如果设置了TableField value 这个字段是实体字段
if(!"".equals(column)){
return column;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
}

View File

@ -1,14 +1,12 @@
package org.jeecg.common.util;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.core.util.ReUtil;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.exception.JeecgSqlInjectionException;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.Set;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -18,50 +16,243 @@ import java.util.regex.Pattern;
* @author zhoujf
*/
@Slf4j
public class SqlInjectionUtil {
public class SqlInjectionUtil {
/**
* sign SQL
* 线 20200501
* sql
*/
private final static String TABLE_DICT_SIGN_SALT = "20200501";
private final static String XSS_STR = "and |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()";
private final static String XSS_STR = "and |exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|--";
/**
* user()
* onlinesql
*/
private final static String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)";
/**正则 show tables*/
private final static String SHOW_TABLES = "show\\s+tables";
private static String specialReportXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |alter |delete |grant |update |drop |master |truncate |declare |--";
/**
* sleep
* sql
*/
private final static Pattern FUN_SLEEP = Pattern.compile("sleep\\(.*\\)", Pattern.CASE_INSENSITIVE);
private static String specialDictSqlXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|--";
/**
* sql
*
*
*/
private final static String[] XSS_REGULAR_STR_ARRAY = new String[]{
"chr\\s*\\(",
"mid\\s*\\(",
" char\\s*\\(",
"sleep\\s*\\(",
"user\\s*\\(",
"show\\s+tables",
"user[\\s]*\\([\\s]*\\)",
"show\\s+databases",
"sleep\\(\\d*\\)",
"sleep\\(.*\\)",
};
/**
* sql
*/
private final static Pattern SQL_ANNOTATION = Pattern.compile("/\\*[\\s\\S]*\\*/");
/**
* sql
*/
private final static String SQL_INJECTION_KEYWORD_TIP = "请注意存在SQL注入关键词---> {}";
private final static String SQL_INJECTION_TIP = "请注意值可能存在SQL注入风险!--->";
private final static String SQL_INJECTION_TIP_VARIABLE = "请注意值可能存在SQL注入风险!---> {}";
/**
* sign
* @param dictCode:
* @param sign:
* @param request:
* @Return: void
* sql
* @param values
*/
private static void checkDictTableSign(String dictCode, String sign, HttpServletRequest request) {
//表字典SQL注入漏洞,签名校验
String accessToken = request.getHeader("X-Access-Token");
String signStr = dictCode + SqlInjectionUtil.TABLE_DICT_SIGN_SALT + accessToken;
String javaSign = SecureUtil.md5(signStr);
if (!javaSign.equals(sign)) {
log.error("表字典SQL注入漏洞签名校验失败 " + sign + "!=" + javaSign+ ",dictCode=" + dictCode);
throw new JeecgBootException("无权限访问!");
}
log.info(" 表字典SQL注入漏洞签名校验成功sign=" + sign + ",dictCode=" + dictCode);
public static void filterContent(String... values) {
filterContent(values, null);
}
/**
*
*
* sql
*
* @param value
* @return
*/
public static void filterContent(String value, String customXssString) {
if (value == null || "".equals(value)) {
return;
}
// 一、校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 转为小写进行后续比较
value = value.toLowerCase().trim();
// 二、SQL注入检测存在绕过风险 (普通文本校验)
//https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE
String[] xssArr = XSS_STR.split("\\|");
for (int i = 0; i < xssArr.length; i++) {
if (value.indexOf(xssArr[i]) > -1) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr[i]);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
// 三、SQL注入检测存在绕过风险 (自定义传入普通文本校验)
if (customXssString != null) {
String[] xssArr2 = customXssString.split("\\|");
for (int i = 0; i < xssArr2.length; i++) {
if (value.indexOf(xssArr2[i]) > -1) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr2[i]);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
}
// 四、SQL注入检测存在绕过风险 (正则校验)
for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
String regular = ".*" + regularOriginal + ".*";
if (Pattern.matches(regular, value)) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, regularOriginal);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
return;
}
/**
* SQL
*
* @param keyword
* @return
*/
@SuppressWarnings("AlibabaUndefineMagicConstant")
private static boolean isExistSqlInjectKeyword(String sql, String keyword) {
if (sql.startsWith(keyword.trim())) {
return true;
} else if (sql.contains(keyword)) {
if (sql.contains(" " + keyword)) {
return true;
} else {
String regularStr = "\\s+\\S+" + keyword;
List<String> resultFindAll = ReUtil.findAll(regularStr, sql, 0, new ArrayList<String>());
for (String res : resultFindAll) {
log.info("isExistSqlInjectKeyword —- 匹配到的SQL注入关键词{}", res);
/**
* SQL(%09 %0A %0D +)
* http://blog.chinaunix.net/uid-12501104-id-2932639.html
* https://www.cnblogs.com/Vinson404/p/7253255.html
* */
if (res.contains("%") || res.contains("+") || res.contains("#") || res.contains("/") || res.contains(")")) {
return true;
}
}
}
}
return false;
}
/**
* sql
*
* @param values
* @return
*/
public static void filterContent(String[] values, String customXssString) {
for (String val : values) {
if (oConvertUtils.isEmpty(val)) {
return;
}
filterContent(val, customXssString);
}
return;
}
/**
*
* SQL
*
* @param value
* @return
*/
public static void specialFilterContentForDictSql(String value) {
String[] xssArr = specialDictSqlXssStr.split("\\|");
if (value == null || "".equals(value)) {
return;
}
// 一、校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
value = value.toLowerCase().trim();
// 二、SQL注入检测存在绕过风险 (普通文本校验)
for (int i = 0; i < xssArr.length; i++) {
if (isExistSqlInjectKeyword(value, xssArr[i])) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr[i]);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
// 三、SQL注入检测存在绕过风险 (正则校验)
for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
String regular = ".*" + regularOriginal + ".*";
if (Pattern.matches(regular, value)) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, regularOriginal);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
return;
}
/**
*
* OnlineSQL
* @param value
* @return
*/
public static void specialFilterContentForOnlineReport(String value) {
String[] xssArr = specialReportXssStr.split("\\|");
if (value == null || "".equals(value)) {
return;
}
// 一、校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
value = value.toLowerCase().trim();
// 二、SQL注入检测存在绕过风险 (普通文本校验)
for (int i = 0; i < xssArr.length; i++) {
if (isExistSqlInjectKeyword(value, xssArr[i])) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssArr[i]);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
// 三、SQL注入检测存在绕过风险 (正则校验)
for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
String regular = ".*" + regularOriginal + ".*";
if (Pattern.matches(regular, value)) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, regularOriginal);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
return;
}
/**
* sql
* @return
*/
public static void checkSqlAnnotation(String str){
Matcher matcher = SQL_ANNOTATION.matcher(str);
if(matcher.find()){
String error = "请注意值可能存在SQL注入风险---> \\*.*\\";
log.error(error);
throw new JeecgSqlInjectionException(error);
}
}
/**
*
* <p>
@ -71,6 +262,10 @@ public class SqlInjectionUtil {
*/
private static Pattern tableNamePattern = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]{0,63}$");
public static String getSqlInjectTableName(String table) {
if(oConvertUtils.isEmpty(table)){
return table;
}
table = table.trim();
/**
*
@ -102,7 +297,7 @@ public class SqlInjectionUtil {
static final Pattern fieldPattern = Pattern.compile("^[a-zA-Z0-9_]+$");
public static String getSqlInjectField(String field) {
if(oConvertUtils.isEmpty(field)){
return null;
return field;
}
field = field.trim();
@ -110,7 +305,7 @@ public class SqlInjectionUtil {
if (field.contains(SymbolConstant.COMMA)) {
return getSqlInjectField(field.split(SymbolConstant.COMMA));
}
/**
*
*
@ -128,6 +323,13 @@ public class SqlInjectionUtil {
return field;
}
/**
*
* :
*
* @param fields
* @return
*/
public static String getSqlInjectField(String... fields) {
for (String s : fields) {
getSqlInjectField(s);
@ -135,233 +337,58 @@ public class SqlInjectionUtil {
return String.join(SymbolConstant.COMMA, fields);
}
/**
* sql
*
* @param value
* @return
*/
public static void filterContent(String value, String customXssString) {
if (value == null || "".equals(value)) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
//SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE
//value = value.replaceAll("/\\*.*\\*/","");
String[] xssArr = XSS_STR.split("\\|");
for (int i = 0; i < xssArr.length; i++) {
if (value.indexOf(xssArr[i]) > -1) {
log.error("请注意存在SQL注入关键词---> {}", xssArr[i]);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
}
//update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (customXssString != null) {
String[] xssArr2 = customXssString.split("\\|");
for (int i = 0; i < xssArr2.length; i++) {
if (value.indexOf(xssArr2[i]) > -1) {
log.error("请注意存在SQL注入关键词---> {}", xssArr2[i]);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
}
}
//update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
return;
}
/**
* sql
* @param values
*/
public static void filterContent(String... values) {
filterContent(values, null);
}
/**
* sql
*
* @param values
* @return
*/
public static void filterContent(String[] values, String customXssString) {
String[] xssArr = XSS_STR.split("\\|");
for (String value : values) {
if (value == null || "".equals(value)) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
//SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE
//value = value.replaceAll("/\\*.*\\*/","");
for (int i = 0; i < xssArr.length; i++) {
if (value.indexOf(xssArr[i]) > -1) {
log.error("请注意存在SQL注入关键词---> {}", xssArr[i]);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
}
//update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (customXssString != null) {
String[] xssArr2 = customXssString.split("\\|");
for (int i = 0; i < xssArr2.length; i++) {
if (value.indexOf(xssArr2[i]) > -1) {
log.error("请注意存在SQL注入关键词---> {}", xssArr2[i]);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
}
}
//update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
}
return;
}
/**
*
* SQL
*
*
*
* @param value
* 1.线
* 2.sql
* @param sortField
* @return
*/
//@Deprecated
public static void specialFilterContentForDictSql(String value) {
String specialXssStr = " exec |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract| insert | select | delete | update | drop | count | chr | mid | master | truncate | char | declare |;|+|user()";
String[] xssArr = specialXssStr.split("\\|");
if (value == null || "".equals(value)) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
//SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE
//value = value.replaceAll("/\\*.*\\*/","");
for (int i = 0; i < xssArr.length; i++) {
if (value.indexOf(xssArr[i]) > -1 || value.startsWith(xssArr[i].trim())) {
log.error("请注意存在SQL注入关键词---> {}", xssArr[i]);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
}
if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
return;
}
/**
*
* OnlineSQL
* @param value
* @return
*/
//@Deprecated
public static void specialFilterContentForOnlineReport(String value) {
String specialXssStr = " exec |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract| insert | delete | update | drop | chr | mid | master | truncate | char | declare |user()";
String[] xssArr = specialXssStr.split("\\|");
if (value == null || "".equals(value)) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
//SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE
//value = value.replaceAll("/\\*.*\\*/"," ");
for (int i = 0; i < xssArr.length; i++) {
if (value.indexOf(xssArr[i]) > -1 || value.startsWith(xssArr[i].trim())) {
log.error("请注意存在SQL注入关键词---> {}", xssArr[i]);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
}
if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
throw new JeecgSqlInjectionException("请注意值可能存在SQL注入风险!--->" + value);
}
return;
}
/**
*
* @param field
* @param clazz
* @return
*/
public static boolean isClassField(String field, Class clazz){
Field[] fields = clazz.getDeclaredFields();
for(int i=0;i<fields.length;i++){
String fieldName = fields[i].getName();
String tableColumnName = oConvertUtils.camelToUnderline(fieldName);
if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
return true;
}
}
return false;
public static String getSqlInjectSortField(String sortField) {
String field = SqlInjectionUtil.getSqlInjectField(oConvertUtils.camelToUnderline(sortField));
return field;
}
/**
*
* @param fieldSet set
* @param clazz
*
*
*
* 1.线
* 2.sql
* @param sortFields
* @return
*/
public static boolean isClassField(Set<String> fieldSet, Class clazz){
Field[] fields = clazz.getDeclaredFields();
for(String field: fieldSet){
boolean exist = false;
for(int i=0;i<fields.length;i++){
String fieldName = fields[i].getName();
String tableColumnName = oConvertUtils.camelToUnderline(fieldName);
if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
exist = true;
break;
}
}
if(!exist){
return false;
}
public static List getSqlInjectSortFields(String... sortFields) {
List list = new ArrayList<String>();
for (String sortField : sortFields) {
list.add(getSqlInjectSortField(sortField));
}
return true;
return list;
}
/**
* sql
* orderBy type
*
* <p>
* 1. asc desc
* 2.sql
*
* @param orderType
* @return
*/
public static void checkSqlAnnotation(String str){
Matcher matcher = SQL_ANNOTATION.matcher(str);
if(matcher.find()){
String error = "请注意值可能存在SQL注入风险---> \\*.*\\";
log.error(error);
throw new JeecgSqlInjectionException(error);
public static String getSqlInjectOrderType(String orderType) {
if (orderType == null) {
return null;
}
// issues/4737 sys/duplicate/check SQL注入 #4737
Matcher sleepMatcher = FUN_SLEEP.matcher(str);
if(sleepMatcher.find()){
String error = "请注意值可能存在SQL注入风险---> sleep";
log.error(error);
throw new JeecgSqlInjectionException(error);
orderType = orderType.trim();
if (CommonConstant.ORDER_TYPE_ASC.equalsIgnoreCase(orderType)) {
return CommonConstant.ORDER_TYPE_ASC;
} else {
return CommonConstant.ORDER_TYPE_DESC;
}
}
}

View File

@ -34,6 +34,21 @@ public class TokenUtils {
}
return token;
}
/**
* request token
* @return
*/
public static String getTokenByRequest() {
String token = null;
try {
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
token = TokenUtils.getTokenByRequest(request);
} catch (Exception e) {
//e.printStackTrace();
}
return token;
}
/**
* request tenantId (ID)

View File

@ -169,7 +169,7 @@ public class FreemarkerParseFactory {
//"where and"
String whereAnd = DataBaseConstant.SQL_WHERE+" and";
//", where"
String commaWhere = SymbolConstant.COMMA+" "+ DataBaseConstant.SQL_WHERE;
String commaWhere = SymbolConstant.COMMA+" "+DataBaseConstant.SQL_WHERE;
//", "
String commaSpace = SymbolConstant.COMMA + " ";
if (sql.endsWith(DataBaseConstant.SQL_WHERE) || sql.endsWith(whereSpace)) {

View File

@ -4,27 +4,68 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* @Description:
* @Description:
* @author: lsq
* @date: 20210809 15:29
* @date: 20230912 15:29
*/
@Slf4j
public class FileTypeFilter {
/**文件后缀*/
private static String[] forbidType = {"jsp","php"};
public class SsrfFileTypeFilter {
/**
*
*/
private final static List<String> FILE_TYPE_WHITE_LIST = new ArrayList<>();
/**初始化文件头类型,不够的自行补充*/
final static HashMap<String, String> FILE_TYPE_MAP = new HashMap<>();
static {
//图片文件
FILE_TYPE_WHITE_LIST.add("jpg");
FILE_TYPE_WHITE_LIST.add("jpeg");
FILE_TYPE_WHITE_LIST.add("png");
FILE_TYPE_WHITE_LIST.add("gif");
FILE_TYPE_WHITE_LIST.add("bmp");
FILE_TYPE_WHITE_LIST.add("svg");
FILE_TYPE_WHITE_LIST.add("ico");
//文本文件
FILE_TYPE_WHITE_LIST.add("txt");
FILE_TYPE_WHITE_LIST.add("doc");
FILE_TYPE_WHITE_LIST.add("docx");
FILE_TYPE_WHITE_LIST.add("pdf");
FILE_TYPE_WHITE_LIST.add("csv");
// FILE_TYPE_WHITE_LIST.add("xml");
//音视频文件
FILE_TYPE_WHITE_LIST.add("mp4");
FILE_TYPE_WHITE_LIST.add("avi");
FILE_TYPE_WHITE_LIST.add("mov");
FILE_TYPE_WHITE_LIST.add("wmv");
FILE_TYPE_WHITE_LIST.add("mp3");
FILE_TYPE_WHITE_LIST.add("wav");
//表格文件
FILE_TYPE_WHITE_LIST.add("xls");
FILE_TYPE_WHITE_LIST.add("xlsx");
//压缩文件
FILE_TYPE_WHITE_LIST.add("zip");
FILE_TYPE_WHITE_LIST.add("rar");
FILE_TYPE_WHITE_LIST.add("7z");
FILE_TYPE_WHITE_LIST.add("tar");
//设置禁止文件的头部标记
FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");
FILE_TYPE_MAP.put("3c3f7068700a0a2f2a2a0a202a205048", "php");
FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");
FILE_TYPE_MAP.put("494e5345525420494e54", "sql");
/* fileTypeMap.put("ffd8ffe000104a464946", "jpg");
fileTypeMap.put("89504e470d0a1a0a0000", "png");
fileTypeMap.put("47494638396126026f01", "gif");
@ -89,17 +130,38 @@ public class FileTypeFilter {
return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
}
/**
*
*
*
* @param filePath
*/
public static void checkDownloadFileType(String filePath) throws IOException {
//文件后缀
String suffix = getFileTypeBySuffix(filePath);
log.info("suffix:{}", suffix);
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
//是否允许下载的文件
if (!isAllowExtension) {
throw new IOException("下载失败,存在非法文件类型:" + suffix);
}
}
/**
*
*
* @param file
*/
public static void fileTypeFilter(MultipartFile file) throws Exception {
public static void checkUploadFileType(MultipartFile file) throws Exception {
//获取文件真是后缀
String suffix = getFileType(file);
for (String type : forbidType) {
if (type.contains(suffix)) {
throw new Exception("上传失败,非法文件类型:" + suffix);
}
log.info("suffix:{}", suffix);
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
//是否允许下载的文件
if (!isAllowExtension) {
throw new Exception("上传失败,存在非法文件类型:" + suffix);
}
}

View File

@ -168,6 +168,17 @@ public class oConvertUtils {
}
}
public static Integer getInteger(Object object, Integer defval) {
if (isEmpty(object)) {
return (defval);
}
try {
return (Integer.parseInt(object.toString()));
} catch (NumberFormatException e) {
return (defval);
}
}
public static Integer getInt(Object object) {
if (isEmpty(object)) {
return null;
@ -702,9 +713,20 @@ public class oConvertUtils {
if (isArray(oldVal)) {
return equalityOfArrays((Object[]) oldVal, (Object[]) newVal);
}else if(oldVal instanceof JSONArray){
return equalityOfJSONArray((JSONArray) oldVal, (JSONArray) newVal);
if(newVal instanceof JSONArray){
return equalityOfJSONArray((JSONArray) oldVal, (JSONArray) newVal);
}else{
if (isEmpty(newVal) && (oldVal == null || ((JSONArray) oldVal).size() == 0)) {
return true;
}
List<Object> arrayStr = Arrays.asList(newVal.toString().split(","));
JSONArray newValArray = new JSONArray(arrayStr);
return equalityOfJSONArray((JSONArray) oldVal, newValArray);
}
}else{
return oldVal.equals(newVal);
}
return oldVal.equals(newVal);
} else {
if (oldVal == null && newVal == null) {
return true;
@ -742,7 +764,7 @@ public class oConvertUtils {
Object[] newValArray = newVal.toArray();
return equalityOfArrays(oldValArray,newValArray);
} else {
if (oldVal == null && newVal == null) {
if ((oldVal == null || oldVal.size() == 0) && (newVal == null || newVal.size() == 0)) {
return true;
} else {
return false;
@ -750,6 +772,38 @@ public class oConvertUtils {
}
}
/**
*
* QQYUN-5212
* @param oldVal
* @param newVal
* @return
*/
public static boolean equalityOfStringArrays(String oldVal, String newVal) {
if(oldVal.equals(newVal)){
return true;
}
if(oldVal.indexOf(",")>=0 && newVal.indexOf(",")>=0){
String[] arr1 = oldVal.split(",");
String[] arr2 = newVal.split(",");
if(arr1.length == arr2.length){
boolean flag = true;
Map<String, Integer> map = new HashMap<>();
for(String s1: arr1){
map.put(s1, 1);
}
for(String s2: arr2){
if(map.get(s2) == null){
flag = false;
break;
}
}
return flag;
}
}
return false;
}
/**
*
*
@ -763,7 +817,7 @@ public class oConvertUtils {
Arrays.sort(newVal);
return Arrays.equals(oldVal, newVal);
} else {
if (oldVal == null && newVal == null) {
if ((oldVal == null || oldVal.length == 0) && (newVal == null || newVal.length == 0)) {
return true;
} else {
return false;
@ -807,4 +861,85 @@ public class oConvertUtils {
}
return json;
}
/**
* List JSONArray
* @return
*/
public static JSONArray list2JSONArray(List<String> list){
if(list==null || list.size()==0){
return null;
}
JSONArray array = new JSONArray();
for(String str: list){
array.add(str);
}
return array;
}
/**
* list
* QQYUN-5326 /
* @return
*/
public static boolean isEqList(List<String> list1, List<String> list2){
if(list1.size() != list2.size()){
return false;
}
for(String str1: list1){
boolean flag = false;
for(String str2: list2){
if(str1.equals(str2)){
flag = true;
break;
}
}
if(!flag){
return false;
}
}
return true;
}
/**
* list1list2
* QQYUN-5326 /
* @param list1
* @param list2
* @return
*/
public static boolean isInList(List<String> list1, List<String> list2){
for(String str1: list1){
boolean flag = false;
for(String str2: list2){
if(str1.equals(str2)){
flag = true;
break;
}
}
if(flag){
return true;
}
}
return false;
}
/**
* MB
* @param uploadCount
* @return
*/
public static Double calculateFileSizeToMb(Long uploadCount){
double count = 0.0;
if(uploadCount>0) {
BigDecimal bigDecimal = new BigDecimal(uploadCount);
//换算成MB
BigDecimal divide = bigDecimal.divide(new BigDecimal(1048576));
count = divide.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
return count;
}
return count;
}
}

View File

@ -11,7 +11,7 @@ import org.apache.commons.fileupload.FileItemStream;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.filter.FileTypeFilter;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.filter.StrAttackFilter;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.web.multipart.MultipartFile;
@ -98,7 +98,7 @@ public class OssBootUtil {
*/
public static String upload(MultipartFile file, String fileDir,String customBucket) throws Exception {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
FileTypeFilter.fileTypeFilter(file);
SsrfFileTypeFilter.checkUploadFileType(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String filePath = null;

View File

@ -0,0 +1,33 @@
package org.jeecg.common.util.sqlInjection;
import net.sf.jsqlparser.parser.CCJSqlParserDefaultVisitor;
import net.sf.jsqlparser.parser.SimpleNode;
import net.sf.jsqlparser.statement.select.UnionOp;
import org.jeecg.common.exception.JeecgSqlInjectionException;
/**
* (AST)
*
* @author guyadong
*/
public class InjectionAstNodeVisitor extends CCJSqlParserDefaultVisitor {
public InjectionAstNodeVisitor() {
}
/**
*
*
* @param node
* @param data
* @return
*/
@Override
public Object visit(SimpleNode node, Object data) {
Object value = node.jjtGetValue();
if (value instanceof UnionOp) {
throw new JeecgSqlInjectionException("DISABLE UNION");
}
return super.visit(node, data);
}
}

View File

@ -0,0 +1,172 @@
package org.jeecg.common.util.sqlInjection;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.jeecg.common.exception.JeecgSqlInjectionException;
import org.jeecg.common.util.sqlInjection.parse.ConstAnalyzer;
import org.jeecg.common.util.sqlInjection.parse.ParserSupport;
/**
* SQLSQL
*
* @author guyadong
*/
public class InjectionSyntaxObjectAnalyzer extends TablesNamesFinder {
/**
*
*/
private static final String DANGROUS_FUNCTIONS = "(sleep|benchmark|extractvalue|updatexml|ST_LatFromGeoHash|ST_LongFromGeoHash|GTID_SUBSET|GTID_SUBTRACT|floor|ST_Pointfromgeohash"
+ "|geometrycollection|multipoint|polygon|multipolygon|linestring|multilinestring)";
private static ThreadLocal<Boolean> disableSubselect = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return true;
}
};
private ConstAnalyzer constAnalyzer = new ConstAnalyzer();
public InjectionSyntaxObjectAnalyzer() {
super();
init(true);
}
@Override
public void visitBinaryExpression(BinaryExpression binaryExpression) {
if (binaryExpression instanceof ComparisonOperator) {
if (isConst(binaryExpression.getLeftExpression()) && isConst(binaryExpression.getRightExpression())) {
/** 禁用恒等式 */
throw new JeecgSqlInjectionException("DISABLE IDENTICAL EQUATION " + binaryExpression);
}
}
super.visitBinaryExpression(binaryExpression);
}
@Override
public void visit(AndExpression andExpression) {
super.visit(andExpression);
checkConstExpress(andExpression.getLeftExpression());
checkConstExpress(andExpression.getRightExpression());
}
@Override
public void visit(OrExpression orExpression) {
super.visit(orExpression);
checkConstExpress(orExpression.getLeftExpression());
checkConstExpress(orExpression.getRightExpression());
}
@Override
public void visit(Function function) {
if (function.getName().matches(DANGROUS_FUNCTIONS)) {
/** 禁用危险函数 */
throw new JeecgSqlInjectionException("DANGROUS FUNCTION: " + function.getName());
}
super.visit(function);
}
@Override
public void visit(WithItem withItem) {
try {
/** 允许 WITH 语句中的子查询 */
disableSubselect.set(false);
super.visit(withItem);
} finally {
disableSubselect.set(true);
}
}
@Override
public void visit(SubSelect subSelect) {
try {
/** 允许语句中的子查询 */
disableSubselect.set(false);
super.visit(subSelect);
} finally {
disableSubselect.set(true);
}
// if (disableSubselect.get()) {
// // 禁用子查询
// throw new JeecgSqlInjectionException("DISABLE subselect " + subSelect);
// }
}
@Override
public void visit(Column tableColumn) {
if (ParserSupport.isBoolean(tableColumn)) {
throw new JeecgSqlInjectionException("DISABLE CONST BOOL " + tableColumn);
}
super.visit(tableColumn);
}
@Override
public void visit(PlainSelect plainSelect) {
if (plainSelect.getSelectItems() != null) {
for (SelectItem item : plainSelect.getSelectItems()) {
item.accept(this);
}
}
if (plainSelect.getFromItem() != null) {
plainSelect.getFromItem().accept(this);
}
if (plainSelect.getJoins() != null) {
for (Join join : plainSelect.getJoins()) {
join.getRightItem().accept(this);
for (Expression e : join.getOnExpressions()) {
e.accept(this);
}
}
}
if (plainSelect.getWhere() != null) {
plainSelect.getWhere().accept(this);
checkConstExpress(plainSelect.getWhere());
}
if (plainSelect.getHaving() != null) {
plainSelect.getHaving().accept(this);
}
if (plainSelect.getOracleHierarchical() != null) {
plainSelect.getOracleHierarchical().accept(this);
}
if (plainSelect.getOrderByElements() != null) {
for (OrderByElement orderByElement : plainSelect.getOrderByElements()) {
orderByElement.getExpression().accept(this);
}
}
if (plainSelect.getGroupBy() != null) {
for (Expression expression : plainSelect.getGroupBy().getGroupByExpressionList().getExpressions()) {
expression.accept(this);
}
}
}
private boolean isConst(Expression expression) {
return constAnalyzer.isConstExpression(expression);
}
private void checkConstExpress(Expression expression) {
if (constAnalyzer.isConstExpression(expression)) {
/** 禁用常量表达式 */
throw new JeecgSqlInjectionException("DISABLE CONST EXPRESSION " + expression);
}
}
}

View File

@ -0,0 +1,65 @@
package org.jeecg.common.util.sqlInjection;
import org.jeecg.common.exception.JeecgSqlInjectionException;
import org.jeecg.common.util.sqlInjection.parse.ParserSupport;
;
/**
* SQL
*
* @author guyadong
* :
* https://blog.csdn.net/10km/article/details/127767358
* https://gitee.com/l0km/sql2java/tree/dev/sql2java-manager/src/main/java/gu/sql2java/parser
*/
public class SqlInjectionAnalyzer {
//启用/关闭注入攻击检查
private boolean injectCheckEnable = true;
//防止SQL注入攻击分析实现
private final InjectionSyntaxObjectAnalyzer injectionChecker;
private final InjectionAstNodeVisitor injectionVisitor;
public SqlInjectionAnalyzer() {
this.injectionChecker = new InjectionSyntaxObjectAnalyzer();
this.injectionVisitor = new InjectionAstNodeVisitor();
}
/**
* /,
*
* @param enable
* @return
*/
public SqlInjectionAnalyzer injectCheckEnable(boolean enable) {
injectCheckEnable = enable;
return this;
}
/**
* SQL{@link JeecgSqlInjectionException}
*
* @param sqlParserInfo
* @throws JeecgSqlInjectionException
*/
public ParserSupport.SqlParserInfo injectAnalyse(ParserSupport.SqlParserInfo sqlParserInfo) throws JeecgSqlInjectionException {
if (null != sqlParserInfo && injectCheckEnable) {
/** SQL注入攻击检查 */
sqlParserInfo.statement.accept(injectionChecker);
sqlParserInfo.simpleNode.jjtAccept(injectionVisitor, null);
}
return sqlParserInfo;
}
/**
* sql
*/
public static void checkSql(String sql,boolean check){
SqlInjectionAnalyzer sqlInjectionAnalyzer = new SqlInjectionAnalyzer();
sqlInjectionAnalyzer.injectCheckEnable(check);
ParserSupport.SqlParserInfo sqlParserInfo = ParserSupport.parse0(sql, null,null);
sqlInjectionAnalyzer.injectAnalyse(sqlParserInfo);
}
}

View File

@ -0,0 +1,601 @@
package org.jeecg.common.util.sqlInjection.parse;
import net.sf.jsqlparser.expression.AllValue;
import net.sf.jsqlparser.expression.AnalyticExpression;
import net.sf.jsqlparser.expression.AnyComparisonExpression;
import net.sf.jsqlparser.expression.ArrayConstructor;
import net.sf.jsqlparser.expression.ArrayExpression;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.CaseExpression;
import net.sf.jsqlparser.expression.CastExpression;
import net.sf.jsqlparser.expression.CollateExpression;
import net.sf.jsqlparser.expression.ConnectByRootOperator;
import net.sf.jsqlparser.expression.DateTimeLiteralExpression;
import net.sf.jsqlparser.expression.DateValue;
import net.sf.jsqlparser.expression.DoubleValue;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.ExtractExpression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.HexValue;
import net.sf.jsqlparser.expression.IntervalExpression;
import net.sf.jsqlparser.expression.JdbcNamedParameter;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.JsonAggregateFunction;
import net.sf.jsqlparser.expression.JsonExpression;
import net.sf.jsqlparser.expression.JsonFunction;
import net.sf.jsqlparser.expression.JsonFunctionExpression;
import net.sf.jsqlparser.expression.KeepExpression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.MySQLGroupConcat;
import net.sf.jsqlparser.expression.NextValExpression;
import net.sf.jsqlparser.expression.NotExpression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.NumericBind;
import net.sf.jsqlparser.expression.OracleHierarchicalExpression;
import net.sf.jsqlparser.expression.OracleHint;
import net.sf.jsqlparser.expression.OracleNamedFunctionParameter;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.RowConstructor;
import net.sf.jsqlparser.expression.RowGetExpression;
import net.sf.jsqlparser.expression.SignedExpression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.TimeKeyExpression;
import net.sf.jsqlparser.expression.TimeValue;
import net.sf.jsqlparser.expression.TimestampValue;
import net.sf.jsqlparser.expression.TimezoneExpression;
import net.sf.jsqlparser.expression.TryCastExpression;
import net.sf.jsqlparser.expression.UserVariable;
import net.sf.jsqlparser.expression.ValueListExpression;
import net.sf.jsqlparser.expression.VariableAssignment;
import net.sf.jsqlparser.expression.WhenClause;
import net.sf.jsqlparser.expression.XMLSerializeExpr;
import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision;
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.conditional.XorExpression;
import net.sf.jsqlparser.expression.operators.relational.Between;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.FullTextSearch;
import net.sf.jsqlparser.expression.operators.relational.GeometryDistance;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor;
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator;
import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator;
import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.AllColumns;
import net.sf.jsqlparser.statement.select.AllTableColumns;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.SubSelect;
/**
*
*
* @author guyadong
*/
public class ConstAnalyzer implements ExpressionVisitor, ItemsListVisitor {
private static ThreadLocal<Boolean> constFlag = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return true;
}
};
@Override
public void visit(NullValue value) {
}
@Override
public void visit(Function function) {
constFlag.set(false);
}
@Override
public void visit(SignedExpression expr) {
expr.getExpression().accept(this);
}
@Override
public void visit(JdbcParameter parameter) {
constFlag.set(false);
}
@Override
public void visit(JdbcNamedParameter parameter) {
constFlag.set(false);
}
@Override
public void visit(DoubleValue value) {
}
@Override
public void visit(LongValue value) {
}
@Override
public void visit(DateValue value) {
}
@Override
public void visit(TimeValue value) {
}
@Override
public void visit(TimestampValue value) {
}
@Override
public void visit(Parenthesis parenthesis) {
parenthesis.getExpression().accept(this);
}
@Override
public void visit(StringValue value) {
}
@Override
public void visit(Addition expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(Division expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(IntegerDivision expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(Multiplication expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(Subtraction expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(AndExpression expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(OrExpression expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(XorExpression expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(Between expr) {
expr.getLeftExpression().accept(this);
expr.getBetweenExpressionStart().accept(this);
expr.getBetweenExpressionEnd().accept(this);
}
@Override
public void visit(EqualsTo expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(GreaterThan expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(GreaterThanEquals expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(InExpression expr) {
if (expr.getLeftExpression() != null) {
expr.getLeftExpression().accept(this);
}
}
@Override
public void visit(IsNullExpression expr) {
expr.getLeftExpression().accept(this);
}
@Override
public void visit(FullTextSearch expr) {
constFlag.set(false);
}
@Override
public void visit(IsBooleanExpression expr) {
expr.getLeftExpression().accept(this);
}
@Override
public void visit(LikeExpression expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(MinorThan expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(MinorThanEquals expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(NotEqualsTo expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(Column column) {
if (!ParserSupport.isBoolean(column)) {
constFlag.set(false);
}
}
@Override
public void visit(SubSelect subSelect) {
constFlag.set(false);
}
@Override
public void visit(CaseExpression expr) {
constFlag.set(false);
}
@Override
public void visit(WhenClause expr) {
constFlag.set(false);
}
@Override
public void visit(ExistsExpression expr) {
constFlag.set(false);
}
@Override
public void visit(AnyComparisonExpression expr) {
constFlag.set(false);
}
@Override
public void visit(Concat expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(Matches expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(BitwiseAnd expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(BitwiseOr expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(BitwiseXor expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(CastExpression expr) {
expr.getLeftExpression().accept(this);
}
@Override
public void visit(TryCastExpression expr) {
constFlag.set(false);
}
@Override
public void visit(Modulo expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(AnalyticExpression expr) {
constFlag.set(false);
}
@Override
public void visit(ExtractExpression expr) {
expr.getExpression().accept(this);
}
@Override
public void visit(IntervalExpression expr) {
constFlag.set(false);
}
@Override
public void visit(OracleHierarchicalExpression expr) {
constFlag.set(false);
}
@Override
public void visit(RegExpMatchOperator expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(ExpressionList expressionList) {
for (Expression expr : expressionList.getExpressions()) {
expr.accept(this);
}
}
@Override
public void visit(NamedExpressionList namedExpressionList) {
for (Expression expr : namedExpressionList.getExpressions()) {
expr.accept(this);
}
}
@Override
public void visit(MultiExpressionList multiExprList) {
for (ExpressionList list : multiExprList.getExpressionLists()) {
visit(list);
}
}
@Override
public void visit(NotExpression notExpr) {
notExpr.getExpression().accept(this);
}
@Override
public void visit(BitwiseRightShift expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(BitwiseLeftShift expr) {
visitBinaryExpression(expr);
}
protected void visitBinaryExpression(BinaryExpression expr) {
expr.getLeftExpression().accept(this);
expr.getRightExpression().accept(this);
}
@Override
public void visit(JsonExpression jsonExpr) {
jsonExpr.getExpression().accept(this);
}
@Override
public void visit(JsonOperator expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(RegExpMySQLOperator expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(UserVariable var) {
constFlag.set(false);
}
@Override
public void visit(NumericBind bind) {
constFlag.set(false);
}
@Override
public void visit(KeepExpression expr) {
for (OrderByElement element : expr.getOrderByElements()) {
element.getExpression().accept(this);
}
}
@Override
public void visit(MySQLGroupConcat groupConcat) {
constFlag.set(false);
}
@Override
public void visit(ValueListExpression valueListExpression) {
for (Expression expr : valueListExpression.getExpressionList().getExpressions()) {
expr.accept(this);
}
}
@Override
public void visit(AllColumns allColumns) {
}
@Override
public void visit(AllTableColumns allTableColumns) {
}
@Override
public void visit(AllValue allValue) {
}
@Override
public void visit(IsDistinctExpression isDistinctExpression) {
visitBinaryExpression(isDistinctExpression);
}
@Override
public void visit(RowGetExpression rowGetExpression) {
rowGetExpression.getExpression().accept(this);
}
@Override
public void visit(HexValue hexValue) {
}
@Override
public void visit(OracleHint hint) {
}
@Override
public void visit(TimeKeyExpression timeKeyExpression) {
}
@Override
public void visit(DateTimeLiteralExpression literal) {
}
@Override
public void visit(NextValExpression nextVal) {
constFlag.set(false);
}
@Override
public void visit(CollateExpression col) {
constFlag.set(false);
}
@Override
public void visit(SimilarToExpression expr) {
visitBinaryExpression(expr);
}
@Override
public void visit(ArrayExpression array) {
array.getObjExpression().accept(this);
if (array.getIndexExpression() != null) {
array.getIndexExpression().accept(this);
}
if (array.getStartIndexExpression() != null) {
array.getStartIndexExpression().accept(this);
}
if (array.getStopIndexExpression() != null) {
array.getStopIndexExpression().accept(this);
}
}
@Override
public void visit(ArrayConstructor aThis) {
for (Expression expression : aThis.getExpressions()) {
expression.accept(this);
}
}
@Override
public void visit(VariableAssignment var) {
constFlag.set(false);
}
@Override
public void visit(XMLSerializeExpr expr) {
constFlag.set(false);
}
@Override
public void visit(TimezoneExpression expr) {
expr.getLeftExpression().accept(this);
}
@Override
public void visit(JsonAggregateFunction expression) {
Expression expr = expression.getExpression();
if (expr != null) {
expr.accept(this);
}
expr = expression.getFilterExpression();
if (expr != null) {
expr.accept(this);
}
}
@Override
public void visit(JsonFunction expression) {
for (JsonFunctionExpression expr : expression.getExpressions()) {
expr.getExpression().accept(this);
}
}
@Override
public void visit(ConnectByRootOperator connectByRootOperator) {
constFlag.set(false);
}
@Override
public void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) {
constFlag.set(false);
}
@Override
public void visit(GeometryDistance geometryDistance) {
visitBinaryExpression(geometryDistance);
}
@Override
public void visit(RowConstructor rowConstructor) {
constFlag.set(false);
}
public boolean isConstExpression(Expression expression) {
if (null != expression) {
constFlag.set(true);
expression.accept(this);
return constFlag.get();
}
return false;
}
}

View File

@ -0,0 +1,177 @@
package org.jeecg.common.util.sqlInjection.parse;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.InvocationTargetException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.base.Throwables;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.exception.JeecgSqlInjectionException;
/**
* sql
*/
@Slf4j
public class ParserSupport {
/**
* SELECT SQL,SELECT
*
* @param sql
* @return
*/
public static Select parseSelect(String sql) {
Statement stmt;
try {
stmt = CCJSqlParserUtil.parse(checkNotNull(sql, "sql is null"));
} catch (JSQLParserException e) {
throw new JeecgBootException(e);
}
checkArgument(stmt instanceof Select, "%s is not SELECT statment", sql);
Select select = (Select) stmt;
SelectBody selectBody = select.getSelectBody();
// 暂时只支持简单的SELECT xxxx FROM ....语句不支持复杂语句如WITH
checkArgument(selectBody instanceof PlainSelect, "ONLY SUPPORT plain select statement %s", sql);
return (Select) stmt;
}
/**
* SELECT SQL,SELECT
*
* @param sql
* @return
*/
public static Select parseSelectUnchecked(String sql) {
try {
return parseSelect(sql);
} catch (Exception e) {
return null;
}
}
/**
* SQL,{@link Statement}
* {@code visitor}AST()
*
* @param sql SQL
* @param visitor {@link SimpleNodeVisitor}{@code null}
* @param sqlSyntaxNormalizer SQL{@code null}
* @throws JSQLParserException SQL
* @see #parse0(String, CCJSqlParserVisitor, SqlSyntaxNormalizer)
*/
public static Statement parse(String sql, CCJSqlParserVisitor visitor, SqlSyntaxNormalizer sqlSyntaxNormalizer) throws JSQLParserException {
return parse0(sql, visitor, sqlSyntaxNormalizer).statement;
}
/**
* {@link CCJSqlParserUtil#parseAST(String)}{@link CCJSqlParserUtil#parse(String)}SQL,
* {@link SqlParserInfo}
* {@code visitor}AST()
*
* @param sql SQL
* @param visitor {@link SimpleNodeVisitor}{@code null}
* @param sqlSyntaxAnalyzer SQL{@code null}
* @throws JSQLParserException SQL
* @see net.sf.jsqlparser.parser.Node#jjtAccept(SimpleNodeVisitor, Object)
*/
public static SqlParserInfo parse0(String sql, CCJSqlParserVisitor visitor, SqlSyntaxNormalizer sqlSyntaxAnalyzer) throws JeecgSqlInjectionException {
//检查是否非select开头暂不支持
if(!sql.toLowerCase().trim().startsWith("select ")) {
log.warn("传入sql 非select开头不支持非select开头的语句解析");
return null;
}
//检查是否存储过程,暂不支持
if(sql.toLowerCase().trim().startsWith("call ")){
log.warn("传入call 开头存储过程,不支持存储过程解析!");
return null;
}
//检查特殊语义的特殊字符,目前检查冒号、$、#三种特殊语义字符
String specialCharacters = "[:$#]";
Pattern pattern = Pattern.compile(specialCharacters);
Matcher matcher = pattern.matcher(sql);
if (matcher.find()) {
sql = sql.replaceAll("[:$#]", "@");
}
checkArgument(null != sql, "sql is null");
boolean allowComplexParsing = CCJSqlParserUtil.getNestingDepth(sql) <= CCJSqlParserUtil.ALLOWED_NESTING_DEPTH;
CCJSqlParser parser = CCJSqlParserUtil.newParser(sql).withAllowComplexParsing(allowComplexParsing);
Statement stmt;
try {
stmt = parser.Statement();
} catch (Exception ex) {
log.error("请注意SQL语法可能存在问题---> {}", ex.getMessage());
throw new JeecgSqlInjectionException("请注意SQL语法可能存在问题:"+sql);
}
if (null != visitor) {
parser.getASTRoot().jjtAccept(visitor, null);
}
if (null != sqlSyntaxAnalyzer) {
stmt.accept(sqlSyntaxAnalyzer.resetChanged());
}
return new SqlParserInfo(stmt.toString(), stmt, (SimpleNode) parser.getASTRoot());
}
/**
* {@link CCJSqlParser}SQL,{@code 'ORDER BY id DESC'}
*
* @param <T>
* @param input
* @param method {@link CCJSqlParser}
* @param targetType
* @return
* @since 3.18.3
*/
public static <T> T parseComponent(String input, String method, Class<T> targetType) {
try {
CCJSqlParser parser = new CCJSqlParser(new StringProvider(input));
try {
return checkNotNull(targetType, "targetType is null").cast(parser.getClass().getMethod(method).invoke(parser));
} catch (InvocationTargetException e) {
Throwables.throwIfUnchecked(e.getTargetException());
throw new RuntimeException(e.getTargetException());
}
} catch (IllegalAccessException | NoSuchMethodException | SecurityException e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
}
/**
* {@link Column}table,true/false()
*
* @param column
*/
public static boolean isBoolean(Column column) {
return null != column && null == column.getTable() &&
Pattern.compile("(true|false)", Pattern.CASE_INSENSITIVE).matcher(column.getColumnName()).matches();
}
public static class SqlParserInfo {
public String nativeSql;
public Statement statement;
public SimpleNode simpleNode;
SqlParserInfo(String nativeSql, Statement statement, SimpleNode simpleNode) {
this.nativeSql = nativeSql;
this.statement = statement;
this.simpleNode = simpleNode;
}
}
}

View File

@ -0,0 +1,37 @@
package org.jeecg.common.util.sqlInjection.parse;
import net.sf.jsqlparser.util.TablesNamesFinder;
/**
* SQL<br>
* SQLSQL
*
*
* @author guyadong
* @since 3.17.0
*/
public class SqlSyntaxNormalizer extends TablesNamesFinder {
protected static final ThreadLocal<Boolean> changed = new ThreadLocal<>();
public SqlSyntaxNormalizer() {
super();
init(true);
}
/**
* {@code true},{@code false}
*/
public boolean changed() {
return Boolean.TRUE.equals(changed.get());
}
/**
* 线{@link #changed}
*/
public SqlSyntaxNormalizer resetChanged() {
changed.remove();
return this;
}
}

View File

@ -0,0 +1,255 @@
package org.jeecg.common.util.sqlparse;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*/
@Slf4j
public class JSqlParserAllTableManager {
private final String sql;
private final Map<String, SelectSqlInfo> allTableMap = new HashMap<>();
/**
*
*/
private final Map<String, String> tableAliasMap = new HashMap<>();
/**
* sql
*/
private String parsedSql = null;
JSqlParserAllTableManager(String selectSql) {
this.sql = selectSql;
}
/**
*
*
* @return
* @throws JSQLParserException
*/
public Map<String, SelectSqlInfo> parse() throws JSQLParserException {
// 1. 创建解析器
CCJSqlParserManager mgr = new CCJSqlParserManager();
// 2. 使用解析器解析sql生成具有层次结构的java类
Statement stmt = mgr.parse(new StringReader(this.sql));
if (stmt instanceof Select) {
Select selectStatement = (Select) stmt;
SelectBody selectBody = selectStatement.getSelectBody();
this.parsedSql = selectBody.toString();
// 3. 解析select查询sql的信息
if (selectBody instanceof PlainSelect) {
PlainSelect plainSelect = (PlainSelect) selectBody;
// 4. 合并 fromItems
List<FromItem> fromItems = new ArrayList<>();
fromItems.add(plainSelect.getFromItem());
// 4.1 处理join的表
List<Join> joins = plainSelect.getJoins();
if (joins != null) {
joins.forEach(join -> fromItems.add(join.getRightItem()));
}
// 5. 处理 fromItems
for (FromItem fromItem : fromItems) {
// 5.1 通过表名的方式from
if (fromItem instanceof Table) {
this.addSqlInfoByTable((Table) fromItem);
}
// 5.2 通过子查询的方式from
else if (fromItem instanceof SubSelect) {
this.handleSubSelect((SubSelect) fromItem);
}
}
// 6. 解析 selectFields
List<SelectItem> selectItems = plainSelect.getSelectItems();
for (SelectItem selectItem : selectItems) {
// 6.1 查询的是全部字段
if (selectItem instanceof AllColumns) {
// 当 selectItem 为 AllColumns 时fromItem 必定为 Table
String tableName = plainSelect.getFromItem(Table.class).getName();
// 此处必定不为空,因为在解析 fromItem 时,已经将表名添加到 allTableMap 中
SelectSqlInfo sqlInfo = this.allTableMap.get(tableName);
assert sqlInfo != null;
// 设置为查询全部字段
sqlInfo.setSelectAll(true);
sqlInfo.setSelectFields(null);
sqlInfo.setRealSelectFields(null);
}
// 6.2 查询的是带表别名( u.* )的全部字段
else if (selectItem instanceof AllTableColumns) {
AllTableColumns allTableColumns = (AllTableColumns) selectItem;
String aliasName = allTableColumns.getTable().getName();
// 通过别名获取表名
String tableName = this.tableAliasMap.get(aliasName);
if (tableName == null) {
tableName = aliasName;
}
SelectSqlInfo sqlInfo = this.allTableMap.get(tableName);
// 如果此处为空,则说明该字段是通过子查询获取的,所以可以不处理,只有实际表才需要处理
if (sqlInfo != null) {
// 设置为查询全部字段
sqlInfo.setSelectAll(true);
sqlInfo.setSelectFields(null);
sqlInfo.setRealSelectFields(null);
}
}
// 6.3 各种字段表达式处理
else if (selectItem instanceof SelectExpressionItem) {
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
Expression expression = selectExpressionItem.getExpression();
Alias alias = selectExpressionItem.getAlias();
this.handleExpression(expression, alias, plainSelect.getFromItem());
}
}
} else {
log.warn("暂时尚未处理该类型的 SelectBody: {}", selectBody.getClass().getName());
throw new JeecgBootException("暂时尚未处理该类型的 SelectBody");
}
} else {
// 非 select 查询sql不做处理
throw new JeecgBootException("非 select 查询sql不做处理");
}
return this.allTableMap;
}
/**
*
*
* @param subSelect
*/
private void handleSubSelect(SubSelect subSelect) {
try {
String subSelectSql = subSelect.getSelectBody().toString();
// 递归调用解析
Map<String, SelectSqlInfo> map = JSqlParserUtils.parseAllSelectTable(subSelectSql);
if (map != null) {
this.assignMap(map);
}
} catch (Exception e) {
log.error("解析子查询出错", e);
}
}
/**
*
*
* @param expression
*/
private void handleExpression(Expression expression, Alias alias, FromItem fromItem) {
// 处理函数式字段 CONCAT(name,'(',age,')')
if (expression instanceof Function) {
Function functionExp = (Function) expression;
List<Expression> expressions = functionExp.getParameters().getExpressions();
for (Expression expItem : expressions) {
this.handleExpression(expItem, null, fromItem);
}
return;
}
// 处理字段上的子查询
if (expression instanceof SubSelect) {
this.handleSubSelect((SubSelect) expression);
return;
}
// 不处理字面量
if (expression instanceof StringValue ||
expression instanceof NullValue ||
expression instanceof LongValue ||
expression instanceof DoubleValue ||
expression instanceof HexValue ||
expression instanceof DateValue ||
expression instanceof TimestampValue ||
expression instanceof TimeValue
) {
return;
}
// 处理字段
if (expression instanceof Column) {
Column column = (Column) expression;
// 查询字段名
String fieldName = column.getColumnName();
String aliasName = fieldName;
if (alias != null) {
aliasName = alias.getName();
}
String tableName;
if (column.getTable() != null) {
// 通过列的表名获取 sqlInfo
// 例如 user.name这里的 tableName 就是 user
tableName = column.getTable().getName();
// 有可能是别名,需要转换为真实表名
if (this.tableAliasMap.get(tableName) != null) {
tableName = this.tableAliasMap.get(tableName);
}
} else {
// 当column的table为空时说明是 fromItem 中的字段
tableName = ((Table) fromItem).getName();
}
SelectSqlInfo $sqlInfo = this.allTableMap.get(tableName);
if ($sqlInfo != null) {
$sqlInfo.addSelectField(aliasName, fieldName);
} else {
log.warn("发生意外情况,未找到表名为 {} 的 SelectSqlInfo", tableName);
}
}
}
/**
* sqlInfo
*
* @param table
*/
private void addSqlInfoByTable(Table table) {
String tableName = table.getName();
// 解析 aliasName
if (table.getAlias() != null) {
this.tableAliasMap.put(table.getAlias().getName(), tableName);
}
SelectSqlInfo sqlInfo = new SelectSqlInfo(this.parsedSql);
sqlInfo.setFromTableName(table.getName());
this.allTableMap.put(sqlInfo.getFromTableName(), sqlInfo);
}
/**
* map
*
* @param source
*/
private void assignMap(Map<String, SelectSqlInfo> source) {
for (Map.Entry<String, SelectSqlInfo> entry : source.entrySet()) {
SelectSqlInfo sqlInfo = this.allTableMap.get(entry.getKey());
if (sqlInfo == null) {
this.allTableMap.put(entry.getKey(), entry.getValue());
} else {
// 合并
if (sqlInfo.getSelectFields() == null) {
sqlInfo.setSelectFields(entry.getValue().getSelectFields());
} else {
sqlInfo.getSelectFields().addAll(entry.getValue().getSelectFields());
}
if (sqlInfo.getRealSelectFields() == null) {
sqlInfo.setRealSelectFields(entry.getValue().getRealSelectFields());
} else {
sqlInfo.getRealSelectFields().addAll(entry.getValue().getRealSelectFields());
}
}
}
}
}

View File

@ -0,0 +1,184 @@
package org.jeecg.common.util.sqlparse;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo;
import java.io.StringReader;
import java.util.List;
import java.util.Map;
@Slf4j
public class JSqlParserUtils {
/**
* selectsql
* map
* key
* value
* <p>
* SELECT a.*,d.age,(SELECT count(1) FROM sys_depart) AS count FROM (SELECT username AS foo, realname FROM sys_user) a, demo d
* {sys_user=[username, realname], demo=[age], sys_depart=[]}
*
* @param selectSql
* @return
*/
public static Map<String, SelectSqlInfo> parseAllSelectTable(String selectSql) throws JSQLParserException {
if (oConvertUtils.isEmpty(selectSql)) {
return null;
}
// log.info("解析查询Sql{}", selectSql);
JSqlParserAllTableManager allTableManager = new JSqlParserAllTableManager(selectSql);
return allTableManager.parse();
}
/**
* selectsql
*
* @param selectSql
* @return
*/
public static SelectSqlInfo parseSelectSqlInfo(String selectSql) throws JSQLParserException {
if (oConvertUtils.isEmpty(selectSql)) {
return null;
}
// log.info("解析查询Sql{}", selectSql);
// 使用 JSqlParer 解析sql
// 1、创建解析器
CCJSqlParserManager mgr = new CCJSqlParserManager();
// 2、使用解析器解析sql生成具有层次结构的java类
Statement stmt = mgr.parse(new StringReader(selectSql));
if (stmt instanceof Select) {
Select selectStatement = (Select) stmt;
// 3、解析select查询sql的信息
return JSqlParserUtils.parseBySelectBody(selectStatement.getSelectBody());
} else {
// 非 select 查询sql不做处理
throw new JeecgBootException("非 select 查询sql不做处理");
}
}
/**
* select sql
*
* @param selectBody
* @return
*/
private static SelectSqlInfo parseBySelectBody(SelectBody selectBody) {
// 简单的select查询
if (selectBody instanceof PlainSelect) {
SelectSqlInfo sqlInfo = new SelectSqlInfo(selectBody);
PlainSelect plainSelect = (PlainSelect) selectBody;
FromItem fromItem = plainSelect.getFromItem();
// 解析 aliasName
if (fromItem.getAlias() != null) {
sqlInfo.setFromTableAliasName(fromItem.getAlias().getName());
}
// 解析 表名
if (fromItem instanceof Table) {
// 通过表名的方式from
Table fromTable = (Table) fromItem;
sqlInfo.setFromTableName(fromTable.getName());
} else if (fromItem instanceof SubSelect) {
// 通过子查询的方式from
SubSelect fromSubSelect = (SubSelect) fromItem;
SelectSqlInfo subSqlInfo = JSqlParserUtils.parseBySelectBody(fromSubSelect.getSelectBody());
sqlInfo.setFromSubSelect(subSqlInfo);
}
// 解析 selectFields
List<SelectItem> selectItems = plainSelect.getSelectItems();
for (SelectItem selectItem : selectItems) {
if (selectItem instanceof AllColumns || selectItem instanceof AllTableColumns) {
// 全部字段
sqlInfo.setSelectAll(true);
sqlInfo.setSelectFields(null);
sqlInfo.setRealSelectFields(null);
break;
} else if (selectItem instanceof SelectExpressionItem) {
// 获取单个查询字段名
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
Expression expression = selectExpressionItem.getExpression();
Alias alias = selectExpressionItem.getAlias();
JSqlParserUtils.handleExpression(sqlInfo, expression, alias);
}
}
return sqlInfo;
} else {
log.warn("暂时尚未处理该类型的 SelectBody: {}", selectBody.getClass().getName());
throw new JeecgBootException("暂时尚未处理该类型的 SelectBody");
}
}
/**
*
*
* @param sqlInfo
* @param expression
* @param alias null
*/
private static void handleExpression(SelectSqlInfo sqlInfo, Expression expression, Alias alias) {
// 处理函数式字段 CONCAT(name,'(',age,')')
if (expression instanceof Function) {
JSqlParserUtils.handleFunctionExpression((Function) expression, sqlInfo);
return;
}
// 处理字段上的子查询
if (expression instanceof SubSelect) {
SubSelect subSelect = (SubSelect) expression;
SelectSqlInfo subSqlInfo = JSqlParserUtils.parseBySelectBody(subSelect.getSelectBody());
// 注:字段上的子查询,必须只查询一个字段,否则会报错,所以可以放心合并
sqlInfo.getSelectFields().addAll(subSqlInfo.getSelectFields());
sqlInfo.getRealSelectFields().addAll(subSqlInfo.getAllRealSelectFields());
return;
}
// 不处理字面量
if (expression instanceof StringValue ||
expression instanceof NullValue ||
expression instanceof LongValue ||
expression instanceof DoubleValue ||
expression instanceof HexValue ||
expression instanceof DateValue ||
expression instanceof TimestampValue ||
expression instanceof TimeValue
) {
return;
}
// 查询字段名
String selectField = expression.toString();
// 实际查询字段名
String realSelectField = selectField;
// 判断是否有别名
if (alias != null) {
selectField = alias.getName();
}
// 获取真实字段名
if (expression instanceof Column) {
Column column = (Column) expression;
realSelectField = column.getColumnName();
}
sqlInfo.addSelectField(selectField, realSelectField);
}
/**
*
*
* @param functionExp
* @param sqlInfo
*/
private static void handleFunctionExpression(Function functionExp, SelectSqlInfo sqlInfo) {
List<Expression> expressions = functionExp.getParameters().getExpressions();
for (Expression expression : expressions) {
JSqlParserUtils.handleExpression(sqlInfo, expression, null);
}
}
}

View File

@ -0,0 +1,101 @@
package org.jeecg.common.util.sqlparse.vo;
import lombok.Data;
import net.sf.jsqlparser.statement.select.SelectBody;
import java.util.HashSet;
import java.util.Set;
/**
* select sql
*/
@Data
public class SelectSqlInfo {
/**
* null
*/
private String fromTableName;
/**
*
*/
private String fromTableAliasName;
/**
* select name from (select * from user) u
* null
*/
private SelectSqlInfo fromSubSelect;
/**
* * null
*/
private Set<String> selectFields;
/**
* * null
*/
private Set<String> realSelectFields;
/**
*
*/
private boolean selectAll;
/**
* SQL
*/
private final String parsedSql;
public SelectSqlInfo(String parsedSql) {
this.parsedSql = parsedSql;
}
public SelectSqlInfo(SelectBody selectBody) {
this.parsedSql = selectBody.toString();
}
public void addSelectField(String selectField, String realSelectField) {
if (this.selectFields == null) {
this.selectFields = new HashSet<>();
}
if (this.realSelectFields == null) {
this.realSelectFields = new HashSet<>();
}
this.selectFields.add(selectField);
this.realSelectFields.add(realSelectField);
}
/**
*
*
* @return
*/
public Set<String> getAllRealSelectFields() {
Set<String> fields = new HashSet<>();
// 递归获取所有字段,起个直观的方法名为:
this.recursiveGetAllFields(this, fields);
return fields;
}
/**
*
*/
private void recursiveGetAllFields(SelectSqlInfo sqlInfo, Set<String> fields) {
if (!sqlInfo.isSelectAll() && sqlInfo.getRealSelectFields() != null) {
fields.addAll(sqlInfo.getRealSelectFields());
}
if (sqlInfo.getFromSubSelect() != null) {
recursiveGetAllFields(sqlInfo.getFromSubSelect(), fields);
}
}
@Override
public String toString() {
return "SelectSqlInfo{" +
"fromTableName='" + fromTableName + '\'' +
", fromSubSelect=" + fromSubSelect +
", aliasName='" + fromTableAliasName + '\'' +
", selectFields=" + selectFields +
", realSelectFields=" + realSelectFields +
", selectAll=" + selectAll +
"}";
}
}

View File

@ -59,7 +59,9 @@ public class AutoPoiDictConfig implements AutoPoiDictServiceI {
for (DictModel t : dictList) {
if(t!=null){
//update-begin---author:liusq Date:20230517 for[issues/4917]excel 导出异常---
if(t!=null && t.getText()!=null && t.getValue()!=null){
//update-end---author:liusq Date:20230517 for[issues/4917]excel 导出异常---
//update-begin---author:scott Date:20211220 for[issues/I4MBB3]@Excel dicText字段的值有下划线时导入功能不能正确解析---
if(t.getValue().contains(EXCEL_SPLIT_TAG)){
String val = t.getValue().replace(EXCEL_SPLIT_TAG,TEMP_EXCEL_SPLIT_TAG);

View File

@ -1,9 +1,6 @@
package org.jeecg.config;
import org.jeecg.config.vo.DomainUrl;
import org.jeecg.config.vo.Elasticsearch;
import org.jeecg.config.vo.Path;
import org.jeecg.config.vo.Shiro;
import org.jeecg.config.vo.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ -29,10 +26,12 @@ public class JeecgBaseConfig {
* local\Miniominio\alioss
*/
private String uploadType;
/**
*
*
*/
private Boolean safeMode = false;
private Firewall firewall;
/**
* shiro
*/
@ -58,6 +57,13 @@ public class JeecgBaseConfig {
*/
private Elasticsearch elasticsearch;
/**
*
* @return
*/
private WeiXinPay weiXinPay;
public Elasticsearch getElasticsearch() {
return elasticsearch;
}
@ -66,12 +72,12 @@ public class JeecgBaseConfig {
this.elasticsearch = elasticsearch;
}
public Boolean getSafeMode() {
return safeMode;
public Firewall getFirewall() {
return firewall;
}
public void setSafeMode(Boolean safeMode) {
this.safeMode = safeMode;
public void setFirewall(Firewall firewall) {
this.firewall = firewall;
}
public String getSignatureSecret() {
@ -129,4 +135,13 @@ public class JeecgBaseConfig {
public void setUploadType(String uploadType) {
this.uploadType = uploadType;
}
public WeiXinPay getWeiXinPay() {
return weiXinPay;
}
public void setWeiXinPay(WeiXinPay weiXinPay) {
this.weiXinPay = weiXinPay;
}
}

View File

@ -0,0 +1,38 @@
package org.jeecg.config.firewall.SqlInjection;
/**
* ::
*
* @Author taoYan
* @Date 2022/3/17 11:21
**/
public interface IDictTableWhiteListHandler {
/**
* true
*
* @param sql
* @return
*/
boolean isPassBySql(String sql);
/**
*
*
* @param dictCodeString
* @return
*/
boolean isPassByDict(String dictCodeString);
boolean isPassByDict(String tableName, String... fields);
/**
* 使
*
* @return
*/
boolean clear();
String getErrorMsg();
}

View File

@ -0,0 +1,102 @@
package org.jeecg.config.firewall.SqlInjection;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.oConvertUtils;
import java.util.HashSet;
import java.util.Set;
/**
*
*/
@Slf4j
public class SysDictTableWhite {
//表名
private String name;
//表的别名
private String alias;
// 字段名集合
private Set<String> fields;
// 是否查询所有字段
private boolean all;
public SysDictTableWhite() {
}
public SysDictTableWhite(String name, String alias) {
this.name = name;
this.alias = alias;
this.all = false;
this.fields = new HashSet<>();
}
public void addField(String field) {
this.fields.add(field);
}
public String getName() {
return name;
}
public Set<String> getFields() {
return new HashSet<>(fields);
}
public void setName(String name) {
this.name = name;
}
public void setFields(Set<String> fields) {
this.fields = fields;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public boolean isAll() {
return all;
}
public void setAll(boolean all) {
this.all = all;
}
/**
*
*
* @param fieldControlString
* @return
*/
public boolean isAllFieldsValid(String fieldControlString) {
//如果白名单中没有配置字段则返回false
String[] controlFields = fieldControlString.split(",");
if (oConvertUtils.isEmpty(fieldControlString)) {
return false;
}
for (String queryField : fields) {
if (oConvertUtils.isIn(queryField, controlFields)) {
log.warn("字典表白名单校验,表【" + name + "】中字段【" + queryField + "】无权限查询");
return false;
}
}
return true;
}
@Override
public String toString() {
return "QueryTable{" +
"name='" + name + '\'' +
", alias='" + alias + '\'' +
", fields=" + fields +
", all=" + all +
'}';
}
}

View File

@ -0,0 +1,19 @@
package org.jeecg.config.firewall.interceptor;
import org.jeecg.config.firewall.interceptor.enums.LowCodeUrlsEnum;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class LowCodeModeConfiguration implements WebMvcConfigurer {
public LowCodeModeInterceptor payInterceptor() {
return new LowCodeModeInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(payInterceptor()).addPathPatterns(LowCodeUrlsEnum.getLowCodeInterceptUrls());
}
}

View File

@ -0,0 +1,113 @@
package org.jeecg.config.firewall.interceptor;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.firewall.interceptor.enums.LowCodeUrlsEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
/**
* dev:prod:线
* <p>
* prodadmin使
* 1.online
* 2.onlinesql
* 3.onlinesql
* 4.线sql
* 5.线sql
*
*
* 1.线sql
*
* @author qinfeng
* @date 20230904
*/
@Slf4j
public class LowCodeModeInterceptor implements HandlerInterceptor {
/**
*
*/
public static final String LOW_CODE_MODE_DEV = "dev";
public static final String LOW_CODE_MODE_PROD = "prod";
@Resource
private JeecgBaseConfig jeecgBaseConfig;
@Autowired
private CommonAPI commonAPI;
/**
*
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//1、验证是否开启低代码开发模式控制
if (jeecgBaseConfig == null) {
jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
}
if (jeecgBaseConfig.getFirewall()!=null && LowCodeModeInterceptor.LOW_CODE_MODE_PROD.equals(jeecgBaseConfig.getFirewall().getLowCodeMode())) {
String requestURI = request.getRequestURI().substring(request.getContextPath().length());
log.info("低代码模式,拦截请求路径:" + requestURI);
LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
Set<String> hasRoles = null;
if (loginUser == null) {
loginUser = commonAPI.getUserByName(JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest()));
//当前登录人拥有的角色
hasRoles = commonAPI.queryUserRoles(loginUser.getUsername());
}
log.info("get loginUser info: {}", loginUser);
log.info("get loginRoles info: {}", hasRoles != null ? hasRoles.toArray() : "空");
//拥有的角色 和 允许开发角色存在交集
boolean hasIntersection = CommonUtils.hasIntersection(hasRoles, CommonConstant.allowDevRoles);
//如果是超级管理员 或者 允许开发的角色,则不做限制
if (loginUser!=null && ("admin".equals(loginUser.getUsername()) || hasIntersection)) {
return true;
}
this.returnErrorMessage(response);
return false;
}
return true;
}
/**
*
*
* @param response
*/
private void returnErrorMessage(HttpServletResponse response) {
//校验失败返回前端
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
try {
out = response.getWriter();
Result<?> result = Result.error("低代码开发模式为发布模式,不允许使用在线配置!!");
out.print(JSON.toJSON(result));
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,104 @@
package org.jeecg.config.firewall.interceptor.enums;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* @author: qinfeng
* @date: 2023/09/04 11:44
*/
public enum LowCodeUrlsEnum {
/**
* online TODO
*/
NEW_LOW_APP_ADD_URL("/online/cgform/api/addAll", "添加online表单"),
NEW_LOW_APP_EDIT_URL("/online/cgform/api/editAll", "编辑online表单"),
ONLINE_DB_SYNC("/online/cgform/api/doDbSynch/**/**", "online表单同步数据库"),
ONLINE_DEL_BATCH("/online/cgform/head/deleteBatch", "online表单批量删除"),
ONLINE_DELETE("/online/cgform/head/delete", "online表单删除"),
ONLINE_REMOVE("/online/cgform/head/removeRecord", "online表单移除"),
ONLINE_COPY("/online/cgform/head/copyOnline", "online表单生成视图"),
ONLINE_TABLE("/online/cgform/head/copyOnlineTable", "online表单复制表"),
ONLINE_BUTTON_AI_TEST("/online/cgform/button/aitest", "online表单自定义按钮生成数据"),
ONLINE_BUTTON_ADD("/online/cgform/button/add", "online表单自定义按钮新增"),
ONLINE_BUTTON_EDIT("/online/cgform/button/edit", "online表单自定义按钮编辑"),
ONLINE_BUTTON_DEL("/online/cgform/button/deleteBatch", "online表单自定义按钮删除"),
ONLINE_ENHANCE_JS("/online/cgform/head/enhanceJs/**", "online表单JS增强"),
ONLINE_ENHANCE_JAVA("/online/cgform/head/enhanceJava/**", "online表单JAVA增强"),
/**
* online
*/
ONLINE_CG_REPORT_ADD("/online/cgreport/head/add", "online报表新增"),
ONLINE_CG_REPORT_EDIT("/online/cgreport/head/editAll", "online报表编辑"),
ONLINE_CG_REPORT_DEL("/online/cgreport/head/delete", "online报表删除"),
ONLINE_CG_REPORT_PARSE_SQL("/online/cgreport/head/parseSql", "online报表SQL解析"),
/**
* online
*/
ONLINE_GRAPH_REPORT_ADD("/online/graphreport/head/add", "online图表新增"),
ONLINE_GRAPH_REPORT_EDIT("/online/graphreport/head/edit", "online图表编辑"),
ONLINE_GRAPH_REPORT_DEL("/online/graphreport/head/deleteBatch", "online图表删除"),
ONLINE_GRAPH_REPORT_PARSE_SQL("/online/cgreport/head/parseSql", "online图表解析SQL"),
/**
*
*/
BIG_SCREEN_DB_ADD("/bigscreen/bigScreenDb/add", "大屏数据源新增"),
BIG_SCREEN_DB_EDIT("/bigscreen/bigScreenDb/edit", "大屏数据源编辑"),
BIG_SCREEN_DB_DEL("/bigscreen/bigScreenDb/delete", "大屏数据源删除"),
BIG_SCREEN_DB_TEST_CONNECTION("/bigscreen/bigScreenDb/testConnection", "大屏数据源连接测试"),
// BIG_SCREEN_SAVE("/bigscreen/visual/save", "大屏新增"),
// BIG_SCREEN_EDIT("/bigscreen/visual/update", "大屏编辑"),
// BIG_SCREEN_COPY("/bigscreen/visual/copy", "大屏复制"),
// BIG_SCREEN_REMOVE("/bigscreen/visual/remove", "大屏移除"),
// BIG_SCREEN_DEL("/bigscreen/visual/deleteById", "大屏删除"),
/**
*
*/
DRAG_DB_ADD("/drag/onlDragDataSource/add", "仪表盘数据源新增"),
DRAG_DB_TEST_CONNECTION("/drag/onlDragDataSource/testConnection", "仪表盘数据源连接测试"),
DRAG_PARSE_SQL("/drag/onlDragDatasetHead/queryFieldBySql", "仪表盘数据集SQL解析"),
DRAG_DATASET_ADD("/drag/onlDragDatasetHead/add", "仪表盘数据集新增");
/**
*
*/
private String url;
private String title;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
LowCodeUrlsEnum(String url, String title) {
this.url = url;
this.title = title;
}
/**
* code
*
* @return
*/
public static List<String> getLowCodeInterceptUrls() {
return Arrays.stream(LowCodeUrlsEnum.values()).map(LowCodeUrlsEnum::getUrl).collect(Collectors.toList());
}
}

View File

@ -127,6 +127,12 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/**/*.js.map", "anon");
filterChainDefinitionMap.put("/**/*.css.map", "anon");
//拖拽仪表盘设计器排除
filterChainDefinitionMap.put("/drag/view", "anon");
filterChainDefinitionMap.put("/drag/page/queryById", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getAllChartData", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getTotalData", "anon");
filterChainDefinitionMap.put("/drag/mock/json/**", "anon");
//大屏模板例子
filterChainDefinitionMap.put("/test/bigScreen/**", "anon");
filterChainDefinitionMap.put("/bigscreen/template1/**", "anon");
@ -246,8 +252,8 @@ public class ShiroConfig {
// redis 单机支持,在集群为空,或者集群无机器时候使用 add by jzyadmin@163.com
if (lettuceConnectionFactory.getClusterConfiguration() == null || lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().isEmpty()) {
RedisManager redisManager = new RedisManager();
redisManager.setHost(lettuceConnectionFactory.getHostName());
redisManager.setPort(lettuceConnectionFactory.getPort());
redisManager.setHost(lettuceConnectionFactory.getHostName() + ":" + lettuceConnectionFactory.getPort());
//(lettuceConnectionFactory.getPort());
redisManager.setDatabase(lettuceConnectionFactory.getDatabase());
redisManager.setTimeout(0);
if (!StringUtils.isEmpty(lettuceConnectionFactory.getPassword())) {

View File

@ -1,12 +1,5 @@
package org.jeecg.config.sign.util;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.http.HttpMethod;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@ -17,6 +10,15 @@ import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.http.HttpMethod;
import com.alibaba.fastjson.JSONObject;
/**
* http
*
@ -46,10 +48,10 @@ public class HttpUtils {
if(deString.contains("%")){
try {
deString = URLDecoder.decode(deString, "UTF-8");
log.info("存在%情况下,执行两次解码 — pathVariable decode: {}",deString);
} catch (Exception e) {
//e.printStackTrace();
}
log.info("存在%情况下,执行两次解码 — pathVariable decode: {}",deString);
}
log.info(" pathVariable decode: {}",deString);
result.put(SignUtil.X_PATH_VARIABLE, deString);

View File

@ -1,80 +0,0 @@
package org.jeecg.config.thirdapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* App
* @author: jeecg-boot
*/
@Configuration
public class ThirdAppConfig {
/**
*
*/
public final static String DINGTALK = "DINGTALK";
/**
*
*/
public final static String WECHAT_ENTERPRISE = "WECHAT_ENTERPRISE";
/**
* App
*/
@Value("${third-app.enabled:false}")
private boolean enabled;
/**
* WECHAT_ENTERPRISEDINGTALK
*/
@Autowired
private ThirdAppTypeConfig type;
public boolean isEnabled() {
return enabled;
}
public ThirdAppConfig setEnabled(boolean enabled) {
this.enabled = enabled;
return this;
}
/**
*
*/
public ThirdAppTypeItemVo getWechatEnterprise() {
return this.type.getWECHAT_ENTERPRISE();
}
/**
*
*/
public ThirdAppTypeItemVo getDingtalk() {
return this.type.getDINGTALK();
}
/**
*
*/
public boolean isWechatEnterpriseEnabled() {
try {
return this.enabled && this.getWechatEnterprise().isEnabled();
} catch (Exception e) {
return false;
}
}
/**
*
*/
public boolean isDingtalkEnabled() {
try {
return this.enabled && this.getDingtalk().isEnabled();
} catch (Exception e) {
return false;
}
}
}

View File

@ -1,26 +0,0 @@
package org.jeecg.config.thirdapp;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* APP
*
* @author sunjianlei
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "third-app.type")
public class ThirdAppTypeConfig {
/**
*
*/
private ThirdAppTypeItemVo WECHAT_ENTERPRISE;
/**
*
*/
private ThirdAppTypeItemVo DINGTALK;
}

View File

@ -1,37 +0,0 @@
package org.jeecg.config.thirdapp;
import lombok.Data;
/**
* App
* @author: jeecg-boot
*/
@Data
public class ThirdAppTypeItemVo {
/**
*
*/
private boolean enabled;
/**
* Key
*/
private String clientId;
/**
* Secret
*/
private String clientSecret;
/**
* ID
*/
private String agentId;
/**
* Secret
*/
private String agentAppSecret;
public int getAgentIdInt() {
return Integer.parseInt(agentId);
}
}

View File

@ -0,0 +1,38 @@
package org.jeecg.config.vo;
/**
*
*
* @author: scott
* @date: 20230905 9:25
*/
public class Firewall {
/**
* (Online)
*/
private Boolean dataSourceSafe = false;
/**
* dev:prod:线
*/
private String lowCodeMode;
// /**
// * 表字典安全模式white:白名单——配置了白名单的表才能通过表字典方式访问black:黑名单——配置了黑名单的表不允许表字典方式访问)
// */
// private String tableDictMode;
public Boolean getDataSourceSafe() {
return dataSourceSafe;
}
public void setDataSourceSafe(Boolean dataSourceSafe) {
this.dataSourceSafe = dataSourceSafe;
}
public String getLowCodeMode() {
return lowCodeMode;
}
public void setLowCodeMode(String lowCodeMode) {
this.lowCodeMode = lowCodeMode;
}
}

View File

@ -0,0 +1,31 @@
package org.jeecg.config.vo;
import lombok.Data;
@Data
public class WeiXinPay {
/**
* id
*/
private String appId;
/**
* id
*/
private String mchId;
/**
*
*/
private String apiKey;
/**
*
*/
private String notifyUrl;
/**
*
*/
private Boolean openVipLimit;
/**
*
*/
private String certPath;
}

View File

@ -0,0 +1,104 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<div class="box-content">
<div class="info-top">
<img src="https://www.jeecg.com/images/logo.png" style="float: left; margin: 0 10px 0 0; width: 32px;height:32px" /><div style="color:#fff"><strong>【重要】流程办理的通知</strong></div>
</div>
<div class="info-wrap">
<div class="tips" style="padding:15px;">
<p style="margin: 10px 0;">
您好,您有一个新的流程任务亟待处理,任务内容如下::
</p>
<table style="width: 400px; border-spacing: 0px; border-collapse: collapse; border: none; margin-top: 20px;"><tbody>
<tr style="height: 45px;">
<td style="width: 150px; height: 40px; background: #F6F6F6;border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
流程名称
</td>
<td style="width: 250px;height: 40px; border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
${bpm_name}<a style="color: #006eff;" href="${url}" target="_blank" rel="noopener">[立刻办理]</a>
</td>
</tr>
<tr style="height: 45px;">
<td style="width: 150px;height: 40px; background: #F6F6F6;border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
催办任务
</td>
<td style="width: 250px;height: 40px; border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
${bpm_task}
</td>
</tr>
<tr style="height: 45px;">
<td style="width: 150px; height: 40px; background: #F6F6F6;border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
催办时间
</td>
<td style="width: 250px;height: 40px; border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
${datetime}
</td>
</tr>
<tr style="height: 45px;">
<td style="width: 150px; height: 40px; background: #F6F6F6;border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
催办内容
</td>
<td style="width: 250px;height: 40px; border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
${remark}
</td>
</tr>
</tbody>
</table>
</div>
<div class="footer">北京国炬平台</div>
</div>
<div style="margin-top: 60px;margin-bottom: 10px;">
<span style="font-size: 13px; font-weight: bold; color: #666;">温馨提醒</span>
<div style="line-height: 24px; margin-top: 10px;">
<div style="font-size: 13px; color: #666;">使用过程中如有任何问题,请联系系统管理员。</div>
</div>
</div>
<div style="width: 600px; margin: 0 auto; margin-top: 50px; font-size: 12px; -webkit-font-smoothing: subpixel-antialiased; text-size-adjust: 100%;">
<p style="text-align: center; line-height: 20.4px; text-size-adjust: 100%; font-family: 'Microsoft YaHei'!important; padding: 0px !important; margin: 0px !important; color: #7e8890 !important;">
<span class="appleLinks">Copyright © 2023-2024 北京国炬信息技术有限公司. 保留所有权利。</span>
</p>
<p style="text-align: center;line-height: 20.4px; text-size-adjust: 100%; font-family: 'Microsoft YaHei'!important; padding: 0px !important; margin: 0px; color: #7e8890 !important; margin-top: 10px;">
<span class="appleLinks">邮件由系统自动发送,请勿直接回复本邮件!</span>
</p>
</div>
</div>
</body>
<style>
.box-content{
width: 80%;
margin: 20px auto;
max-width: 800px;
min-width: 600px;
}
.info-top{
padding: 15px 25px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
background: #4ea3f2;
color: #fff;
overflow: hidden;
line-height: 32px;
}
.info-wrap{
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border:1px solid #ddd;
overflow: hidden;
padding: 15px 15px 20px;
}
.footer{
text-align: right;
color: #999;
padding: 0 15px 15px;
}
</style>
</html>

View File

@ -0,0 +1,101 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<div class="box-content">
<div class="info-top">
<img src="https://jeecgdev.oss-cn-beijing.aliyuncs.com/temp/logo(1)_1697180761742.png"
style="float: left; margin: 0 10px 0 0; width: 32px;height:32px"/>
<div style="color:#fff"><strong>【重要】流程办理的通知</strong></div>
</div>
<div class="info-wrap">
<div class="tips" style="padding:15px;">
<p style="margin: 10px 0;">
您好, ${REALNAME}<br>您有一个新的流程任务需要处理,任务内容如下:
</p>
<table style="width: 400px; border-spacing: 0px; border-collapse: collapse; border: none; margin-top: 20px;">
<tbody>
<tr style="height: 45px;">
<td style="width: 150px;height: 40px; background: #F6F6F6;border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
业务标题
</td>
<td style="width: 250px;height: 40px; border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
${title}
</td>
</tr>
<tr style="height: 45px;">
<td style="width: 150px; height: 40px; background: #F6F6F6;border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
流程名称
</td>
<td style="width: 250px;height: 40px; border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
${name}
<a style="color: #006eff;" href="${url}" target="_blank" rel="noopener">[立刻办理]</a>
</td>
</tr>
<tr style="height: 45px;">
<td style="width: 150px; height: 40px; background: #F6F6F6;border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
任务节点
</td>
<td style="width: 250px;height: 40px; border: 1px solid #DBDBDB; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">
${task}
</td>
</tr>
</tbody>
</table>
</div>
<div class="footer">北京国炬平台</div>
</div>
<div style="margin-top: 60px;margin-bottom: 10px;">
<span style="font-size: 13px; font-weight: bold; color: #666;">温馨提醒</span>
<div style="line-height: 24px; margin-top: 10px;">
<div style="font-size: 13px; color: #666;">使用过程中如有任何问题,请联系系统管理员。</div>
</div>
</div>
<div style="width: 600px; margin: 0 auto; margin-top: 50px; font-size: 12px; -webkit-font-smoothing: subpixel-antialiased; text-size-adjust: 100%;">
<p style="text-align: center; line-height: 20.4px; text-size-adjust: 100%; font-family: 'Microsoft YaHei'!important; padding: 0px !important; margin: 0px !important; color: #7e8890 !important;">
<span class="appleLinks">Copyright © 2023-2024 北京国炬科技股份有限公司. 保留所有权利。</span>
</p>
<p style="text-align: center;line-height: 20.4px; text-size-adjust: 100%; font-family: 'Microsoft YaHei'!important; padding: 0px !important; margin: 0px; color: #7e8890 !important; margin-top: 10px;">
<span class="appleLinks">邮件由系统自动发送,请勿直接回复本邮件!</span>
</p>
</div>
</div>
</body>
<style>
.box-content {
width: 80%;
margin: 20px auto;
max-width: 800px;
min-width: 600px;
}
.info-top {
padding: 15px 25px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
background: #4ea3f2;
color: #fff;
overflow: hidden;
line-height: 32px;
}
.info-wrap {
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border: 1px solid #ddd;
overflow: hidden;
padding: 15px 15px 20px;
}
.footer {
text-align: right;
color: #999;
padding: 0 15px 15px;
}
</style>
</html>

View File

@ -0,0 +1,73 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<div class="box-content">
<div class="info-top">
<img src="https://jeecgdev.oss-cn-beijing.aliyuncs.com/temp/logo(1)_1697180761742.png" style="float: left; margin: 0 10px 0 0; width: 32px;height:32px" /><div style="color:#fff"><strong>【重要】新数据提醒</strong></div>
</div>
<div class="info-wrap">
<div class="tips" style="padding:15px;">
<p style="margin: 10px 0;">
尊敬的 ${userName} 用户,您好:
</p>
你的表单 <a style="color: #006eff;" href="${formLink}" target="_blank" rel="noopener">【${formName}】</a>
在 ${createTime} 新增了1条数据。
${dataMarkdown}
<p>
如需查看更多请点击
<a style="color: #006eff;" href="${moreLink}" target="_blank" rel="noopener">[查看所有数据]</a>
</p>
</div>
<div class="footer">北京国炬平台</div>
<div class="footer" id="currentTime"></div>
</div>
<div style="width: 600px; margin: 0 auto; margin-top: 50px; font-size: 12px; -webkit-font-smoothing: subpixel-antialiased; text-size-adjust: 100%;">
<p style="text-align: center; line-height: 20.4px; text-size-adjust: 100%; font-family: 'Microsoft YaHei'!important; padding: 0px !important; margin: 0px !important; color: #7e8890 !important;">
<span class="appleLinks">Copyright © 2023-2024 北京国炬科技股份有限公司. 保留所有权利。</span>
</p>
<p style="text-align: center;line-height: 20.4px; text-size-adjust: 100%; font-family: 'Microsoft YaHei'!important; padding: 0px !important; margin: 0px; color: #7e8890 !important; margin-top: 10px;">
<span class="appleLinks">邮件由系统自动发送,请勿直接回复本邮件!</span>
</p>
</div>
</div>
</body>
<style>
.box-content{
width: 80%;
margin: 20px auto;
max-width: 800px;
min-width: 600px;
}
.info-top{
padding: 15px 25px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
background: #4ea3f2;
color: #fff;
overflow: hidden;
line-height: 32px;
}
.info-wrap{
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border:1px solid #ddd;
overflow: hidden;
padding: 15px 15px 20px;
}
.footer{
text-align: right;
color: #999;
padding: 0 15px 15px;
}
</style>
</html>

View File

@ -0,0 +1,75 @@
package org.jeecg.test.sqlinjection;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* SQL
* @author: liusq
* @date: 20230908
*/
@Slf4j
public class TestInjectWithSqlParser {
/**
*
*
* @param sql
* @return
*/
private boolean isExistSqlInject(String sql) {
try {
SqlInjectionUtil.specialFilterContentForOnlineReport(sql);
return false;
} catch (Exception e) {
log.info("===================================================");
return true;
}
}
@Test
public void test() throws JSQLParserException {
//不存在sql注入
assertFalse(isExistSqlInject("select * from fm_time where dept_id=:sqlparamsmap.id and time=:sqlparamsmap.time"));
assertFalse(isExistSqlInject("select * from test"));
assertFalse(isExistSqlInject("select load_file(\"C:\\\\benben.txt\")"));
assertFalse(isExistSqlInject("WITH SUB1 AS (SELECT user FROM t1) SELECT * FROM T2 WHERE id > 123 "));
//存在sql注入
assertTrue(isExistSqlInject("or 1= 1 --"));
assertTrue(isExistSqlInject("select * from test where sleep(%23)"));
assertTrue(isExistSqlInject("select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));"));
assertTrue(isExistSqlInject("select * from users;show databases;"));
assertTrue(isExistSqlInject("select * from dc_device where id=1 and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13"));
assertTrue(isExistSqlInject("update user set name = '123'"));
assertTrue(isExistSqlInject("SELECT * FROM users WHERE username = 'admin' AND password = '123456' OR 1=1;--"));
assertTrue(isExistSqlInject("select * from users where id=1 and (select count(*) from information_schema.tables where table_schema='数据库名')>4 %23"));
assertTrue(isExistSqlInject("select * from dc_device where sleep(5) %23"));
assertTrue(isExistSqlInject("select * from dc_device where id in (select id from other)"));
assertTrue(isExistSqlInject("select * from dc_device where id in (select id from other)"));
assertTrue(isExistSqlInject("select * from dc_device where 2=2.0 or 2 != 4"));
assertTrue(isExistSqlInject("select * from dc_device where 1!=2.0"));
assertTrue(isExistSqlInject("select * from dc_device where id=floor(2.0)"));
assertTrue(isExistSqlInject("select * from dc_device where not true"));
assertTrue(isExistSqlInject("select * from dc_device where 1 or id > 0"));
assertTrue(isExistSqlInject("select * from dc_device where 'tom' or id > 0"));
assertTrue(isExistSqlInject("select * from dc_device where '-2.3' "));
assertTrue(isExistSqlInject("select * from dc_device where 2 "));
assertTrue(isExistSqlInject("select * from dc_device where (3+2) "));
assertTrue(isExistSqlInject("select * from dc_device where -1 IS TRUE"));
assertTrue(isExistSqlInject("select * from dc_device where 'hello' is null "));
assertTrue(isExistSqlInject("select * from dc_device where '2022-10-31' and id > 0"));
assertTrue(isExistSqlInject("select * from dc_device where id > 0 or 1!=2.0 "));
assertTrue(isExistSqlInject("select * from dc_device where id > 0 or 1 in (1,3,4) "));
assertTrue(isExistSqlInject("select * from dc_device UNION select name from other"));
assertTrue(isExistSqlInject("(SELECT 6240 FROM (SELECT(SLEEP(5))and 1=2)vidl)"));
}
}

View File

@ -0,0 +1,50 @@
package org.jeecg.test.sqlinjection;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* SQL
* @author: liusq
* @date: 20230908
*/
@Slf4j
public class TestSqlInjectForDict {
/**
*
*
* @param sql
* @return
*/
private boolean isExistSqlInject(String sql) {
try {
SqlInjectionUtil.specialFilterContentForDictSql(sql);
return false;
} catch (Exception e) {
log.info("===================================================");
return true;
}
}
@Test
public void test() throws JSQLParserException {
//不存在sql注入
assertFalse(isExistSqlInject("sys_user,realname,id"));
assertFalse(isExistSqlInject("oa_officialdoc_organcode,organ_name,id"));
assertFalse(isExistSqlInject("onl_cgform_head where table_type!=3 and copy_type=0,table_txt,table_name"));
assertFalse(isExistSqlInject("onl_cgform_head where copy_type = 0,table_txt,table_name"));
//存在sql注入
assertTrue(isExistSqlInject("or 1= 1 --"));
assertTrue(isExistSqlInject("select * from test where sleep(%23)"));
}
}

View File

@ -0,0 +1,60 @@
package org.jeecg.test.sqlinjection;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* SQL
* @author: liusq
* @date: 20230908
*/
@Slf4j
public class TestSqlInjectForOnlineReport {
/**
*
*
* @param sql
* @return
*/
private boolean isExistSqlInject(String sql) {
try {
SqlInjectionUtil.specialFilterContentForOnlineReport(sql);
return false;
} catch (Exception e) {
log.info("===================================================");
return true;
}
}
@Test
public void test() throws JSQLParserException {
//不存在sql注入
assertFalse(isExistSqlInject("select * from fm_time where dept_id=:sqlparamsmap.id and time=:sqlparamsmap.time"));
assertFalse(isExistSqlInject("select * from test"));
assertFalse(isExistSqlInject("select load_file(\"C:\\\\benben.txt\")"));
assertFalse(isExistSqlInject("select * from dc_device where id in (select id from other)"));
assertFalse(isExistSqlInject("select * from dc_device UNION select name from other"));
//存在sql注入
assertTrue(isExistSqlInject("(SELECT 6240 FROM (SELECT(SLEEP(5))and 1=2)vidl)"));
assertTrue(isExistSqlInject("or 1= 1 --"));
assertTrue(isExistSqlInject("select * from test where sleep(%23)"));
assertTrue(isExistSqlInject("select * from test where SLEEP(3)"));
assertTrue(isExistSqlInject("select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));"));
assertTrue(isExistSqlInject("select * from users;show databases;"));
assertTrue(isExistSqlInject("select * from dc_device where id=1 and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13"));
assertTrue(isExistSqlInject("update user set name = '123'"));
assertTrue(isExistSqlInject("SELECT * FROM users WHERE username = 'admin' AND password = '123456' OR 1=1;--"));
assertTrue(isExistSqlInject("select * from users where id=1 and (select count(*) from information_schema.tables where table_schema='数据库名')>4 %23"));
assertTrue(isExistSqlInject("select * from dc_device where sleep(5) %23"));
}
}

View File

@ -0,0 +1,103 @@
package org.jeecg.test.sqlinjection;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* @Description: SQL
* @author: scott
* @date: 20230814 9:55
*/
public class TestSqlInjection {
/**
* html
*/
@Test
public void testSpecialSQL() {
String tableName = "sys_user t";
//解决使用参数tableName=sys_user t&复测,漏洞仍然存在
if (tableName.contains(" ")) {
tableName = tableName.substring(0, tableName.indexOf(" "));
}
//【issues/4393】 sys_user , (sys_user), sys_user%20, %60sys_user%60
String reg = "\\s+|\\(|\\)|`";
tableName = tableName.replaceAll(reg, "");
System.out.println(tableName);
}
/**
* sqlsql
* <p>
* mybatis plus
*/
@Test
public void sqlInjectionCheck() {
String sql = "select * from sys_user";
System.out.println(SqlInjectionUtils.check(sql));
}
/**
* sqlSLEEP
* <p>
* mybatisPlus
*/
@Test
public void sqlSleepCheck() {
SqlInjectionUtil.checkSqlAnnotation("(SELECT 6240 FROM (SELECT(SLEEP(5))and 1=2)vidl)");
}
/**
* sqlsql
* <p>
*
*/
@Test
public void sqlInjectionCheck2() {
String sql = "select * from sys_user";
SqlInjectionUtil.specialFilterContentForOnlineReport(sql);
}
/**
* 线
* <p>
*
*/
@Test
public void testFieldSpecification() {
List<String> list = new ArrayList();
list.add("Hello World!");
list.add("Hello%20World!");
list.add("HelloWorld!");
list.add("Hello World");
list.add("age");
list.add("user_name");
list.add("user_name%20");
list.add("user_name%20 ");
for (String input : list) {
boolean containsSpecialChars = isValidString(input);
System.out.println("input:" + input + " ,包含空格和特殊字符: " + containsSpecialChars);
}
}
/**
* 线
*
* @param input
* @return
*/
private static boolean isValidString(String input) {
Pattern pattern = Pattern.compile("^[a-zA-Z0-9_]+$");
return pattern.matcher(input).matches();
}
}

View File

@ -0,0 +1,109 @@
package org.jeecg.test.sqlparse;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.sqlparse.JSqlParserUtils;
import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo;
import org.junit.Test;
import java.util.Map;
/**
* JSqlParserUtils
*/
public class JSqlParserUtilsTest {
private static final String[] sqlList = new String[]{
"select * from sys_user",
"select u.* from sys_user u",
"select u.*, c.name from sys_user u, demo c",
"select u.age, c.name from sys_user u, demo c",
"select sex, age, c.name from sys_user, demo c",
// 别名测试
"select username as realname from sys_user",
"select username as realname, u.realname as aaa, u.id bbb from sys_user u",
// 不存在真实地查询字段
"select count(1) from sys_user",
// 函数式字段
"select max(sex), id from sys_user",
// 复杂嵌套函数式字段
"select CONCAT(CONCAT(' _ ', sex), ' - ' , birthday) as info, id from sys_user",
// 更复杂的嵌套函数式字段
"select CONCAT(CONCAT(101,'_',NULL, DATE(create_time),'_',sex),' - ',birthday) as info, id from sys_user",
// 子查询SQL
"select u.name1 as name2 from (select username as name1 from sys_user) u",
// 多层嵌套子查询SQL
"select u2.name2 as name3 from (select u1.name1 as name2 from (select username as name1 from sys_user) u1) u2",
// 字段子查询SQL
"select id, (select username as name1 from sys_user u2 where u1.id = u2.id) as name2 from sys_user u1",
// 带条件的SQL不解析where条件里的字段但不影响解析查询字段
"select username as name1 from sys_user where realname LIKE '%张%'",
// 多重复杂关联表查询解析包含的表为sys_user, sys_depart, sys_dict_item, demo
"" +
"SELECT " +
" u.*, d.age, sd.item_text AS sex, (SELECT count(sd.id) FROM sys_depart sd) AS count " +
"FROM " +
" (SELECT sd.username AS foo, sd.realname FROM sys_user sd) u, " +
" demo d " +
"LEFT JOIN sys_dict_item AS sd ON d.sex = sd.item_value " +
"WHERE sd.dict_id = '3d9a351be3436fbefb1307d4cfb49bf2'",
};
@Test
public void testParseSelectSql() {
System.out.println("-----------------------------------------");
for (String sql : sqlList) {
System.out.println("待测试的sql" + sql);
try {
// 解析所有的表名key=表名value=解析后的sql信息
Map<String, SelectSqlInfo> parsedMap = JSqlParserUtils.parseAllSelectTable(sql);
assert parsedMap != null;
for (Map.Entry<String, SelectSqlInfo> entry : parsedMap.entrySet()) {
System.out.println("表名:" + entry.getKey());
this.printSqlInfo(entry.getValue(), 1);
}
} catch (JSQLParserException e) {
System.out.println("SQL解析出现异常" + e.getMessage());
}
System.out.println("-----------------------------------------");
}
}
private void printSqlInfo(SelectSqlInfo sqlInfo, int level) {
String beforeStr = this.getBeforeStr(level);
if (sqlInfo.getFromTableName() == null) {
// 子查询
System.out.println(beforeStr + "子查询:" + sqlInfo.getFromSubSelect().getParsedSql());
this.printSqlInfo(sqlInfo.getFromSubSelect(), level + 1);
} else {
// 非子查询
System.out.println(beforeStr + "查询的表名:" + sqlInfo.getFromTableName());
}
if (oConvertUtils.isNotEmpty(sqlInfo.getFromTableAliasName())) {
System.out.println(beforeStr + "查询的表别名:" + sqlInfo.getFromTableAliasName());
}
if (sqlInfo.isSelectAll()) {
System.out.println(beforeStr + "查询的字段:*");
} else {
System.out.println(beforeStr + "查询的字段:" + sqlInfo.getSelectFields());
System.out.println(beforeStr + "真实的字段:" + sqlInfo.getRealSelectFields());
if (sqlInfo.getFromTableName() == null) {
System.out.println(beforeStr + "所有的字段(包括子查询):" + sqlInfo.getAllRealSelectFields());
}
}
}
// 打印前缀,根据层级来打印
private String getBeforeStr(int level) {
if (level == 0) {
return "";
}
StringBuilder beforeStr = new StringBuilder();
for (int i = 0; i < level; i++) {
beforeStr.append(" ");
}
beforeStr.append("- ");
return beforeStr.toString();
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-parent</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.5.5</version>
<version>3.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.5.5</version>
<version>3.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -6,6 +6,8 @@ import org.jeecg.common.api.dto.DataLogDTO;
import org.jeecg.common.api.dto.OnlineAuthDTO;
import org.jeecg.common.api.dto.message.*;
import org.jeecg.common.constant.ServiceNameConstants;
import org.jeecg.common.constant.enums.EmailTemplateEnum;
import org.jeecg.common.desensitization.annotation.SensitiveDecode;
import org.jeecg.common.system.api.factory.SysBaseAPIFallbackFactory;
import org.jeecg.common.system.vo.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
@ -72,6 +74,7 @@ public interface ISysBaseAPI extends CommonAPI {
* @param id
* @return
*/
@SensitiveDecode
@GetMapping("/sys/api/getUserById")
LoginUser getUserById(@RequestParam("id") String id);
@ -132,14 +135,14 @@ public interface ISysBaseAPI extends CommonAPI {
/**
* 13
* @param table
* @param tableFilterSql
* @param text
* @param code
* @return
*/
@Override
@GetMapping("/sys/api/queryTableDictItemsByCode")
List<DictModel> queryTableDictItemsByCode(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code);
List<DictModel> queryTableDictItemsByCode(@RequestParam("tableFilterSql") String tableFilterSql, @RequestParam("text") String text, @RequestParam("code") String code);
/**
* 14 id -->value,departName -->text
@ -258,7 +261,7 @@ public interface ISysBaseAPI extends CommonAPI {
* @return
*/
@GetMapping("/sys/api/queryAllUserByIds")
public List<LoginUser> queryAllUserByIds(@RequestParam("userIds") String[] userIds);
public List<UserAccountInfo> queryAllUserByIds(@RequestParam("userIds") String[] userIds);
/**
* 28
@ -275,7 +278,7 @@ public interface ISysBaseAPI extends CommonAPI {
* @return
*/
@GetMapping("/sys/api/queryUserByNames")
List<LoginUser> queryUserByNames(@RequestParam("userNames")String[] userNames);
List<UserAccountInfo> queryUserByNames(@RequestParam("userNames")String[] userNames);
/**
@ -365,6 +368,7 @@ public interface ISysBaseAPI extends CommonAPI {
* @return LoginUser
*/
@Override
@SensitiveDecode
@GetMapping("/sys/api/getUserByName")
LoginUser getUserByName(@RequestParam("username") String username);
@ -450,6 +454,17 @@ public interface ISysBaseAPI extends CommonAPI {
*/
@GetMapping("/sys/api/sendEmailMsg")
void sendEmailMsg(@RequestParam("email")String email,@RequestParam("title")String title,@RequestParam("content")String content);
/**
* html
*
* @param email
* @param title
* @param emailTemplateEnum
* @param params
*/
@GetMapping("/sys/api/sendHtmlTemplateEmail")
void sendHtmlTemplateEmail(@RequestParam("email") String email, @RequestParam("title") String title, @RequestParam("emailEnum") EmailTemplateEnum emailTemplateEnum, @RequestParam("params") JSONObject params);
/**
* 41 id
* @param orgCode
@ -476,6 +491,17 @@ public interface ISysBaseAPI extends CommonAPI {
@GetMapping("/sys/api/loadDictItem")
List<String> loadDictItem(@RequestParam("dictCode") String dictCode, @RequestParam("keys") String keys);
/**
*
*
* @param originalAppId ID
* @param appId ID
* @param tenantId ID
* @return Map<String, String> Map<, >
*/
@GetMapping("/sys/api/copyLowAppDict")
Map<String, String> copyLowAppDict(@RequestParam("originalAppId") String originalAppId, @RequestParam("appId") String appId, @RequestParam("tenantId") String tenantId);
/**
* 44 code
*
@ -577,9 +603,138 @@ public interface ISysBaseAPI extends CommonAPI {
* @param loginUser
* @return
*/
@PutMapping("/updateAvatar")
@PutMapping("/sys/api/updateAvatar")
void updateAvatar(@RequestBody LoginUser loginUser);
@GetMapping("/sendAppChatSocket")
@GetMapping("/sys/api/sendAppChatSocket")
void sendAppChatSocket(@RequestParam(name="userId") String userId);
/**
* idcode
* @param id
* @return
*/
@GetMapping("/sys/api/getRoleCode")
String getRoleCodeById(String id);
/**
* roleCode
*
* @param roleCodes
* @return
*/
@GetMapping("/sys/api/queryRoleDictByCode")
List<DictModel> queryRoleDictByCode(@RequestParam(name = "roleCodes") String roleCodes);
/**
*
* @param superQuery
* @param matchType
* @return
*/
@GetMapping("/sys/api/queryUserBySuperQuery")
List<JSONObject> queryUserBySuperQuery(@RequestParam(name="superQuery")String superQuery,@RequestParam(name="matchType")String matchType);
/**
* ID
* @param id
* @return JSONObject
*/
@GetMapping("/sys/api/queryUserById")
JSONObject queryUserById(String id);
/**
*
* @param superQuery
* @param matchType
* @return
*/
@GetMapping("/sys/api/queryDeptBySuperQuery")
List<JSONObject> queryDeptBySuperQuery(@RequestParam(name="superQuery")String superQuery,@RequestParam(name="matchType")String matchType);
/**
*
* @param superQuery
* @param matchType
* @return
*/
@GetMapping("/sys/api/queryRoleBySuperQuery")
List<JSONObject> queryRoleBySuperQuery(@RequestParam(name="superQuery")String superQuery,@RequestParam(name="matchType")String matchType);
/**
* IDID
* @param tenantId ID
* @return List<String>
*/
@GetMapping("/sys/api/selectUserIdByTenantId")
List<String> selectUserIdByTenantId(@RequestParam("tenantId")String tenantId);
/**
* IDID
* @param deptIds
* @return
*/
@GetMapping("/sys/api/queryUserIdsByDeptIds")
List<String> queryUserIdsByDeptIds(List<String> deptIds);
/**
* ID
* @param deptIds
* @return
*/
@GetMapping("/sys/api/queryUserAccountsByDeptIds")
List<String> queryUserAccountsByDeptIds(List<String> deptIds);
/**
* ID
* @param roleCodes
* @return
*/
@GetMapping("/sys/api/queryUserIdsByRoleds")
List<String> queryUserIdsByRoleds(List<String> roleCodes);
/**
* IDID
* @param positionIds
* @return
*/
@GetMapping("/sys/api/queryUserIdsByPositionIds")
List<String> queryUserIdsByPositionIds(List<String> positionIds);
/**
*
*
* @param orgCode
* @return
*/
@GetMapping("/sys/api/getUserAccountsByDepCode")
public List<String> getUserAccountsByDepCode(@RequestParam("orgCode")String orgCode);
/**
* sql
*
* @param selectSql
* @return
*/
@GetMapping("/sys/api/dictTableWhiteListCheckBySql")
boolean dictTableWhiteListCheckBySql(@RequestParam("selectSql") String selectSql);
/**
*
*
* @param tableOrDictCode dictCode
* @param fields dictCodenull
* @return
*/
@GetMapping("/sys/api/dictTableWhiteListCheckByDict")
boolean dictTableWhiteListCheckByDict(
@RequestParam("tableOrDictCode") String tableOrDictCode,
@RequestParam(value = "fields", required = false) String[] fields
);
}

View File

@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.dto.DataLogDTO;
import org.jeecg.common.api.dto.OnlineAuthDTO;
import org.jeecg.common.api.dto.message.*;
import org.jeecg.common.constant.enums.EmailTemplateEnum;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.*;
@ -91,7 +92,7 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
}
@Override
public List<DictModel> queryTableDictItemsByCode(String table, String text, String code) {
public List<DictModel> queryTableDictItemsByCode(String tableFilterSql, String text, String code) {
return null;
}
@ -163,7 +164,7 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
}
@Override
public List<LoginUser> queryAllUserByIds(String[] userIds) {
public List<UserAccountInfo> queryAllUserByIds(String[] userIds) {
return null;
}
@ -173,7 +174,7 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
}
@Override
public List<LoginUser> queryUserByNames(String[] userNames) {
public List<UserAccountInfo> queryUserByNames(String[] userNames) {
return null;
}
@ -298,6 +299,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
}
@Override
public void sendHtmlTemplateEmail(String email, String title, EmailTemplateEnum emailTemplateEnum, JSONObject params) {
}
@Override
public List<Map> getDeptUserByOrgCode(String orgCode) {
return null;
@ -318,6 +324,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public Map<String, String> copyLowAppDict(String originalAppId, String appId, String tenantId) {
return null;
}
@Override
public List<DictModel> getDictItems(String dictCode) {
return null;
@ -351,4 +362,75 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
public void sendAppChatSocket(String userId) {
}
@Override
public String getRoleCodeById(String id) {
return null;
}
@Override
public List<DictModel> queryRoleDictByCode(String roleCodes) {
return null;
}
@Override
public List<JSONObject> queryUserBySuperQuery(String superQuery, String matchType) {
return null;
}
@Override
public JSONObject queryUserById(String id) {
return null;
}
@Override
public List<JSONObject> queryDeptBySuperQuery(String superQuery, String matchType) {
return null;
}
@Override
public List<JSONObject> queryRoleBySuperQuery(String superQuery, String matchType) {
return null;
}
@Override
public List<String> selectUserIdByTenantId(String tenantId) {
return null;
}
@Override
public List<String> queryUserIdsByDeptIds(List<String> deptIds) {
return null;
}
@Override
public List<String> queryUserAccountsByDeptIds(List<String> deptIds) {
return null;
}
@Override
public List<String> queryUserIdsByRoleds(List<String> roleCodes) {
return null;
}
@Override
public List<String> queryUserIdsByPositionIds(List<String> positionIds) {
return null;
}
@Override
public List<String> getUserAccountsByDepCode(String orgCode) {
return null;
}
@Override
public boolean dictTableWhiteListCheckBySql(String selectSql) {
return false;
}
@Override
public boolean dictTableWhiteListCheckByDict(String tableOrDictCode, String[] fields) {
return false;
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.5.5</version>
<version>3.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,6 +5,7 @@ import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.dto.DataLogDTO;
import org.jeecg.common.api.dto.OnlineAuthDTO;
import org.jeecg.common.api.dto.message.*;
import org.jeecg.common.constant.enums.EmailTemplateEnum;
import org.jeecg.common.system.vo.*;
import java.util.List;
@ -19,7 +20,7 @@ import java.util.Set;
*/
public interface ISysBaseAPI extends CommonAPI {
//=======OLD 系统消息推送接口============================
/**
* 1
* @param message 使 category()2
@ -43,28 +44,29 @@ public interface ISysBaseAPI extends CommonAPI {
* @param message 使
*/
void sendBusTemplateAnnouncement(BusTemplateMessageDTO message);
/**
* 5
* @param templateDTO 使
* @return
*/
String parseTemplateByCode(TemplateDTO templateDTO);
//=======OLD 系统消息推送接口============================
//update-begin---author:taoyan ---date:20220705 for支持自定义推送类型邮件、钉钉、企业微信、系统消息-----------
//=======TY NEW 自定义消息推送接口,邮件、钉钉、企业微信、系统消息============================
/**
*
* NEW:
* @param message
*/
void sendTemplateMessage(MessageDTO message);
/**
*
* NEW
* @param templateCode
* @return
*/
String getTemplateContent(String templateCode);
//update-begin---author:taoyan ---date:20220705 for支持自定义推送类型邮件、钉钉、企业微信、系统消息-----------
//=======TY NEW 自定义消息推送接口,邮件、钉钉、企业微信、系统消息============================
/**
* 6id
@ -216,7 +218,7 @@ public interface ISysBaseAPI extends CommonAPI {
* @param userIds id
* @return
*/
public List<LoginUser> queryAllUserByIds(String[] userIds);
public List<UserAccountInfo> queryAllUserByIds(String[] userIds);
/**
* 29
@ -231,7 +233,50 @@ public interface ISysBaseAPI extends CommonAPI {
* @param userNames
* @return
*/
List<LoginUser> queryUserByNames(String[] userNames);
List<UserAccountInfo> queryUserByNames(String[] userNames);
/**
*
* @param superQuery
* @param matchType
* @return
*/
List<JSONObject> queryUserBySuperQuery(String superQuery,String matchType);
/**
* ID
* @param id
* @return
*/
JSONObject queryUserById(String id);
/**
*
* @param superQuery
* @param matchType
* @return
*/
List<JSONObject> queryDeptBySuperQuery(String superQuery,String matchType);
/**
*
* @param superQuery
* @param matchType
* @return
*/
List<JSONObject> queryRoleBySuperQuery(String superQuery,String matchType);
/**
* IDID
* @param tenantId ID
* @return List<String>
*/
List<String> selectUserIdByTenantId(String tenantId);
/**
@ -304,6 +349,16 @@ public interface ISysBaseAPI extends CommonAPI {
* @param content
*/
void sendEmailMsg(String email,String title,String content);
/**
* 40
*
* @param email
* @param title
* @param emailTemplateEnum
* @param params
*/
void sendHtmlTemplateEmail(String email, String title, EmailTemplateEnum emailTemplateEnum, JSONObject params);
/**
* 41
* @param orgCode
@ -327,6 +382,16 @@ public interface ISysBaseAPI extends CommonAPI {
*/
List<String> loadDictItem(String dictCode, String keys);
/**
*
*
* @param originalAppId ID
* @param appId ID
* @param tenantId ID
* @return Map<String, String> Map<, >
*/
Map<String, String> copyLowAppDict(String originalAppId, String appId, String tenantId);
/**
* code
*
@ -383,4 +448,73 @@ public interface ISysBaseAPI extends CommonAPI {
* @param userId
*/
void sendAppChatSocket(String userId);
/**
* idcode
* @param id
* @return
*/
String getRoleCodeById(String id);
/**
* roleCode
*
* @param roleCodes
* @return
*/
List<DictModel> queryRoleDictByCode(String roleCodes);
/**
* IDID
* @param deptIds
* @return
*/
List<String> queryUserIdsByDeptIds(List<String> deptIds);
/**
* ID
* @param deptIds
* @return
*/
List<String> queryUserAccountsByDeptIds(List<String> deptIds);
/**
* ID
* @param roleCodes
* @return
*/
List<String> queryUserIdsByRoleds(List<String> roleCodes);
/**
* IDID
* @param positionIds
* @return
*/
List<String> queryUserIdsByPositionIds(List<String> positionIds);
/**
*
*
* @param orgCode
* @return
*/
public List<String> getUserAccountsByDepCode(String orgCode);
/**
* sql
*
* @param selectSql
* @return
*/
boolean dictTableWhiteListCheckBySql(String selectSql);
/**
*
*
* @param tableOrDictCode dictCode
* @param fields dictCodenull
* @return
*/
boolean dictTableWhiteListCheckByDict(String tableOrDictCode, String... fields);
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-module-system</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.5.5</version>
<version>3.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-module-system</artifactId>
<version>3.5.5</version>
<version>3.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,279 @@
package org.jeecg.config.firewall.SqlInjection.impl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgSqlInjectionException;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.sqlparse.JSqlParserUtils;
import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.firewall.SqlInjection.IDictTableWhiteListHandler;
import org.jeecg.config.firewall.interceptor.LowCodeModeInterceptor;
import org.jeecg.modules.system.entity.SysTableWhiteList;
import org.jeecg.modules.system.security.DictQueryBlackListHandler;
import org.jeecg.modules.system.service.ISysTableWhiteListService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.net.URLDecoder;
import java.util.*;
/**
*
*/
@Slf4j
@Component("dictTableWhiteListHandlerImpl")
public class DictTableWhiteListHandlerImpl implements IDictTableWhiteListHandler {
/**
* key-
* value-
* --
* whiteTablesRuleMap.put("sys_user", "*") sys_user
* whiteTablesRuleMap.put("sys_user", "username,password") sys_userusernamepassword
*/
private static final Map<String, String> whiteTablesRuleMap = new HashMap<>();
/**
* LowCode dev
*/
private static Boolean LOW_CODE_IS_DEV = null;
@Autowired
private ISysTableWhiteListService sysTableWhiteListService;
@Autowired
private JeecgBaseConfig jeecgBaseConfig;
/**
* whiteTablesRuleMap
*/
private void init() {
// 如果当前为dev模式则每次都查询数据库防止缓存
if (this.isDev()) {
DictTableWhiteListHandlerImpl.whiteTablesRuleMap.clear();
}
// 如果map为空则从数据库中查询
if (DictTableWhiteListHandlerImpl.whiteTablesRuleMap.isEmpty()) {
Map<String, String> ruleMap = sysTableWhiteListService.getAllConfigMap();
log.info("表字典白名单初始化完成:{}", ruleMap);
DictTableWhiteListHandlerImpl.whiteTablesRuleMap.putAll(ruleMap);
}
}
@Override
public boolean isPassBySql(String sql) {
Map<String, SelectSqlInfo> parsedMap = null;
try {
parsedMap = JSqlParserUtils.parseAllSelectTable(sql);
} catch (Exception e) {
log.warn("校验sql语句解析报错{}", e.getMessage());
}
// 如果sql有问题则肯定执行不了所以直接返回true
if (parsedMap == null) {
return true;
}
log.info("获取select sql信息 {} ", parsedMap);
// 遍历当前sql中的所有表名如果有其中一个表或表的字段不在白名单中则不通过
for (Map.Entry<String, SelectSqlInfo> entry : parsedMap.entrySet()) {
SelectSqlInfo sqlInfo = entry.getValue();
if (sqlInfo.isSelectAll()) {
log.warn("查询语句中包含 * 字段,暂时先通过");
continue;
}
Set<String> queryFields = sqlInfo.getAllRealSelectFields();
// 校验表名和字段是否允许查询
String tableName = entry.getKey();
if (!this.checkWhiteList(tableName, queryFields)) {
return false;
}
}
return true;
}
@Override
public boolean isPassByDict(String dictCodeString) {
if (oConvertUtils.isEmpty(dictCodeString)) {
return true;
}
try {
// 针对转义字符进行解码
dictCodeString = URLDecoder.decode(dictCodeString, "UTF-8");
} catch (Exception e) {
log.error(e.getMessage(), e);
this.throwException("字典code解码失败可能是使用了非法字符请检查");
}
dictCodeString = dictCodeString.trim();
String[] arr = dictCodeString.split(SymbolConstant.COMMA);
// 获取表名
String tableName = this.getTableName(arr[0]);
// 获取查询字段
arr = Arrays.copyOfRange(arr, 1, arr.length);
// distinct的作用是去重相当于 Set<String>
String[] fields = Arrays.stream(arr).map(String::trim).distinct().toArray(String[]::new);
// 校验表名和字段是否允许查询
return this.isPassByDict(tableName, fields);
}
@Override
public boolean isPassByDict(String tableName, String... fields) {
if (oConvertUtils.isEmpty(tableName)) {
return true;
}
if (fields == null || fields.length == 0) {
fields = new String[]{"*"};
}
String sql = "select " + String.join(",", fields) + " from " + tableName;
log.info("字典拼接的查询SQL{}", sql);
try {
// 进行SQL解析
JSqlParserUtils.parseSelectSqlInfo(sql);
} catch (Exception e) {
// 如果SQL解析失败则通过字段名和表名进行校验
return checkWhiteList(tableName, new HashSet<>(Arrays.asList(fields)));
}
// 通过SQL解析进行校验可防止SQL注入
return this.isPassBySql(sql);
}
/**
*
*
* @param tableName
* @param queryFields
* @return
*/
public boolean checkWhiteList(String tableName, Set<String> queryFields) {
this.init();
// 1、判断“表名”是否通过校验如果为空则未通过校验
if (oConvertUtils.isEmpty(tableName)) {
log.error("白名单校验:表名为空");
this.throwException();
}
// 统一转成小写
tableName = tableName.toLowerCase();
String allowFieldStr = DictTableWhiteListHandlerImpl.whiteTablesRuleMap.get(tableName);
log.info("checkWhiteList tableName: {}", tableName);
if (oConvertUtils.isEmpty(allowFieldStr)) {
// 如果是dev模式自动向数据库里添加数据
if (this.isDev()) {
this.autoAddWhiteList(tableName, String.join(",", queryFields));
allowFieldStr = DictTableWhiteListHandlerImpl.whiteTablesRuleMap.get(tableName);
} else {
// prod模式下直接抛出异常
log.error("白名单校验:表\"{}\"未通过校验", tableName);
this.throwException();
}
}
// 2、判断“字段名”是否通过校验
// 统一转成小写
allowFieldStr = allowFieldStr.toLowerCase();
Set<String> allowFields = new HashSet<>(Arrays.asList(allowFieldStr.split(",")));
// 需要合并的字段
Set<String> waitMergerFields = new HashSet<>();
for (String field : queryFields) {
if(oConvertUtils.isEmpty(field)){
continue;
}
// 统一转成小写
field = field.toLowerCase();
// 如果允许的字段里不包含查询的字段,则直接抛出异常
if (!allowFields.contains(field)) {
// 如果是dev模式记录需要合并的字段
if (this.isDev()) {
waitMergerFields.add(field);
} else {
log.error("白名单校验:字段 {} 不在 {} 范围内,拒绝访问!", field, allowFields);
this.throwException();
}
}
}
// 自动向数据库中合并未通过的字段
if (!waitMergerFields.isEmpty()) {
this.autoAddWhiteList(tableName, String.join(",", waitMergerFields));
}
log.info("白名单校验:查询表\"{}\",查询字段 {} 通过校验", tableName, queryFields);
return true;
}
/**
*
*
* @param tableName
* @param allowFieldStr
*/
private void autoAddWhiteList(String tableName, String allowFieldStr) {
try {
SysTableWhiteList entity = sysTableWhiteListService.autoAdd(tableName, allowFieldStr);
DictTableWhiteListHandlerImpl.whiteTablesRuleMap.put(tableName, entity.getFieldName());
log.warn("表\"{}\"未通过校验,且当前为 dev 模式,已自动向数据库中增加白名单数据。查询字段:{}", tableName, allowFieldStr);
} catch (Exception e) {
log.error("表\"{}\"未通过校验,且当前为 dev 模式,但自动向数据库中增加白名单数据失败,请排查后重试。错误原因:{}", tableName, e.getMessage(), e);
this.throwException();
}
}
/**
* LowCode dev
*/
private boolean isDev() {
if (DictTableWhiteListHandlerImpl.LOW_CODE_IS_DEV == null) {
if (this.jeecgBaseConfig.getFirewall() != null) {
String lowCodeMode = this.jeecgBaseConfig.getFirewall().getLowCodeMode();
DictTableWhiteListHandlerImpl.LOW_CODE_IS_DEV = LowCodeModeInterceptor.LOW_CODE_MODE_DEV.equals(lowCodeMode);
} else {
// 如果没有 firewall 配置,则默认为 false
DictTableWhiteListHandlerImpl.LOW_CODE_IS_DEV = false;
}
}
return DictTableWhiteListHandlerImpl.LOW_CODE_IS_DEV;
}
@Override
public boolean clear() {
DictTableWhiteListHandlerImpl.whiteTablesRuleMap.clear();
return true;
}
/**
* wheretable name
*
* @param str
* @see DictQueryBlackListHandler#getTableName(String)
*/
@SuppressWarnings("JavadocReference")
private String getTableName(String str) {
String[] arr = str.split("\\s+(?i)where\\s+");
String tableName = arr[0].trim();
//【20230814】解决使用参数tableName=sys_user t&复测,漏洞仍然存在
if (tableName.contains(".")) {
tableName = tableName.substring(tableName.indexOf(".") + 1, tableName.length()).trim();
}
if (tableName.contains(" ")) {
tableName = tableName.substring(0, tableName.indexOf(" ")).trim();
}
//【issues/4393】 sys_user , (sys_user), sys_user%20, %60sys_user%60
String reg = "\\s+|\\(|\\)|`";
return tableName.replaceAll(reg, "");
}
private void throwException() throws JeecgSqlInjectionException {
this.throwException(this.getErrorMsg());
}
private void throwException(String message) throws JeecgSqlInjectionException {
if (oConvertUtils.isEmpty(message)) {
message = this.getErrorMsg();
}
log.error(message);
throw new JeecgSqlInjectionException(message);
}
@Override
public String getErrorMsg() {
return "白名单校验未通过!";
}
}

View File

@ -6,9 +6,8 @@ import org.jeecg.common.api.dto.DataLogDTO;
import org.jeecg.common.api.dto.OnlineAuthDTO;
import org.jeecg.common.api.dto.message.*;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.desensitization.util.SensitiveInfoUtil;
import org.jeecg.common.system.vo.*;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.modules.system.security.DictQueryBlackListHandler;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.service.impl.SysBaseApiImpl;
import org.springframework.beans.factory.annotation.Autowired;
@ -33,10 +32,6 @@ public class SystemApiController {
@Autowired
private ISysUserService sysUserService;
@Autowired
private DictQueryBlackListHandler dictQueryBlackListHandler;
/**
*
* @param message 使 category()2
@ -98,7 +93,14 @@ public class SystemApiController {
*/
@GetMapping("/getUserByName")
public LoginUser getUserByName(@RequestParam("username") String username){
return sysBaseApi.getUserByName(username);
LoginUser loginUser = sysBaseApi.getUserByName(username);
//用户信息加密
try {
SensitiveInfoUtil.handlerObject(loginUser, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return loginUser;
}
/**
@ -108,7 +110,14 @@ public class SystemApiController {
*/
@GetMapping("/getUserById")
LoginUser getUserById(@RequestParam("id") String id){
return sysBaseApi.getUserById(id);
LoginUser loginUser = sysBaseApi.getUserById(id);
//用户信息加密
try {
SensitiveInfoUtil.handlerObject(loginUser, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return loginUser;
}
/**
@ -300,7 +309,7 @@ public class SystemApiController {
* @return
*/
@GetMapping("/queryAllUserByIds")
public List<LoginUser> queryAllUserByIds(@RequestParam("userIds") String[] userIds){
public List<UserAccountInfo> queryAllUserByIds(@RequestParam("userIds") String[] userIds){
return sysBaseApi.queryAllUserByIds(userIds);
}
@ -341,7 +350,7 @@ public class SystemApiController {
* @return
*/
@GetMapping("/queryUserByNames")
public List<LoginUser> queryUserByNames(@RequestParam("userNames")String[] userNames){
public List<UserAccountInfo> queryUserByNames(@RequestParam("userNames")String[] userNames){
return sysBaseApi.queryUserByNames(userNames);
}
@ -527,13 +536,22 @@ public class SystemApiController {
*/
@GetMapping("/loadDictItem")
public List<String> loadDictItem(@RequestParam("dictCode") String dictCode, @RequestParam("keys") String keys) {
if(!dictQueryBlackListHandler.isPass(dictCode)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
return sysBaseApi.loadDictItem(dictCode, keys);
}
/**
*
*
* @param originalAppId ID
* @param appId ID
* @param tenantId ID
* @return Map<String, String> Map<, >
*/
@GetMapping("/sys/api/copyLowAppDict")
Map<String, String> copyLowAppDict(@RequestParam("originalAppId") String originalAppId, @RequestParam("appId") String appId, @RequestParam("tenantId") String tenantId) {
return sysBaseApi.copyLowAppDict(originalAppId, appId, tenantId);
}
/**
* code
*
@ -543,10 +561,6 @@ public class SystemApiController {
*/
@GetMapping("/getDictItems")
public List<DictModel> getDictItems(@RequestParam("dictCode") String dictCode) {
if(!dictQueryBlackListHandler.isPass(dictCode)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
return sysBaseApi.getDictItems(dictCode);
}
@ -571,10 +585,6 @@ public class SystemApiController {
*/
@GetMapping("/loadDictItemByKeyword")
public List<DictModel> loadDictItemByKeyword(@RequestParam("dictCode") String dictCode, @RequestParam("keyword") String keyword, @RequestParam(value = "pageSize", required = false) Integer pageSize) {
if(!dictQueryBlackListHandler.isPass(dictCode)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
return sysBaseApi.loadDictItemByKeyword(dictCode, keyword, pageSize);
}
@ -592,19 +602,14 @@ public class SystemApiController {
/**
*
* @param table
* @param tableFilterSql where
* @param text
* @param code
* @return
*/
@GetMapping("/queryTableDictItemsByCode")
List<DictModel> queryTableDictItemsByCode(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code){
String str = table+","+text+","+code;
if(!dictQueryBlackListHandler.isPass(str)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
return sysBaseApi.queryTableDictItemsByCode(table, text, code);
List<DictModel> queryTableDictItemsByCode(@RequestParam("tableFilterSql") String tableFilterSql, @RequestParam("text") String text, @RequestParam("code") String code){
return sysBaseApi.queryTableDictItemsByCode(tableFilterSql, text, code);
}
/**
@ -617,14 +622,6 @@ public class SystemApiController {
*/
@GetMapping("/queryFilterTableDictInfo")
List<DictModel> queryFilterTableDictInfo(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("filterSql") String filterSql){
String str = table+","+text+","+code;
if(!dictQueryBlackListHandler.isPass(str)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
String[] arr = new String[]{table, text, code};
SqlInjectionUtil.filterContent(arr);
SqlInjectionUtil.specialFilterContentForDictSql(filterSql);
return sysBaseApi.queryFilterTableDictInfo(table, text, code, filterSql);
}
@ -640,11 +637,6 @@ public class SystemApiController {
@Deprecated
@GetMapping("/queryTableDictByKeys")
public List<String> queryTableDictByKeys(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("keyArray") String[] keyArray){
String str = table+","+text+","+code;
if(!dictQueryBlackListHandler.isPass(str)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
return sysBaseApi.queryTableDictByKeys(table, text, code, keyArray);
}
@ -659,13 +651,6 @@ public class SystemApiController {
*/
@GetMapping("/translateDictFromTable")
public String translateDictFromTable(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("key") String key){
String str = table+","+text+","+code;
if(!dictQueryBlackListHandler.isPass(str)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
String[] arr = new String[]{table, text, code, key};
SqlInjectionUtil.filterContent(arr);
return sysBaseApi.translateDictFromTable(table, text, code, key);
}
@ -682,11 +667,6 @@ public class SystemApiController {
*/
@GetMapping("/translateDictFromTableByKeys")
public List<DictModel> translateDictFromTableByKeys(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("keys") String keys) {
String str = table+","+text+","+code;
if(!dictQueryBlackListHandler.isPass(str)){
log.error(dictQueryBlackListHandler.getError());
return null;
}
return this.sysBaseApi.translateDictFromTableByKeys(table, text, code, keys);
}
@ -746,7 +726,27 @@ public class SystemApiController {
this.sysBaseApi.sendAppChatSocket(userId);
}
/**
* roleCode
*
* @param roleCodes
* @return
*/
@GetMapping("/queryRoleDictByCode")
public List<DictModel> queryRoleDictByCode(@RequestParam(name = "roleCodes") String roleCodes) {
return this.sysBaseApi.queryRoleDictByCode(roleCodes);
}
/**
*
* @param id
* @return
*/
@GetMapping("/getRoleCode")
public String getRoleCode(@RequestParam("id") String id){
return this.sysBaseApi.getRoleCodeById(id);
}
/**
* VUEN-2584issuesql
*
@ -763,5 +763,138 @@ public class SystemApiController {
}
return Result.error("校验失败sql解析异常" + msg);
}
/**
*
* @param superQuery
* @param matchType
* @return
*/
@GetMapping("/queryUserBySuperQuery")
public List<JSONObject> queryUserBySuperQuery(@RequestParam("superQuery") String superQuery, @RequestParam("matchType") String matchType) {
return sysBaseApi.queryUserBySuperQuery(superQuery,matchType);
}
/**
* id
* @param id
* @return
*/
@GetMapping("/queryUserById")
public JSONObject queryUserById(@RequestParam("id") String id) {
return sysBaseApi.queryUserById(id);
}
/**
*
* @param superQuery
* @param matchType
* @return
*/
@GetMapping("/queryDeptBySuperQuery")
public List<JSONObject> queryDeptBySuperQuery(@RequestParam("superQuery") String superQuery, @RequestParam("matchType") String matchType) {
return sysBaseApi.queryDeptBySuperQuery(superQuery,matchType);
}
/**
*
* @param superQuery
* @param matchType
* @return
*/
@GetMapping("/queryRoleBySuperQuery")
public List<JSONObject> queryRoleBySuperQuery(@RequestParam("superQuery") String superQuery, @RequestParam("matchType") String matchType) {
return sysBaseApi.queryRoleBySuperQuery(superQuery,matchType);
}
/**
* IDID
* @param tenantId ID
* @return List<String>
*/
@GetMapping("/selectUserIdByTenantId")
public List<String> selectUserIdByTenantId(@RequestParam("tenantId") String tenantId) {
return sysBaseApi.selectUserIdByTenantId(tenantId);
}
/**
* IDID
* @param deptIds
* @return
*/
@GetMapping("/sys/api/queryUserIdsByDeptIds")
public List<String> queryUserIdsByDeptIds(@RequestParam("deptIds") List<String> deptIds){
return sysBaseApi.queryUserIdsByDeptIds(deptIds);
}
/**
* IDID
* @param deptIds
* @return
*/
@GetMapping("/sys/api/queryUserAccountsByDeptIds")
public List<String> queryUserAccountsByDeptIds(@RequestParam("deptIds") List<String> deptIds){
return sysBaseApi.queryUserAccountsByDeptIds(deptIds);
}
/**
* ID
* @param roleCodes
* @return
*/
@GetMapping("/sys/api/queryUserIdsByRoleds")
public List<String> queryUserIdsByRoleds(@RequestParam("roleCodes") List<String> roleCodes){
return sysBaseApi.queryUserIdsByRoleds(roleCodes);
}
/**
* IDID
* @param positionIds
* @return
*/
@GetMapping("/sys/api/queryUserIdsByPositionIds")
public List<String> queryUserIdsByPositionIds(@RequestParam("positionIds") List<String> positionIds){
return sysBaseApi.queryUserIdsByPositionIds(positionIds);
}
/**
*
*
* @param orgCode
* @return
*/
@GetMapping("/sys/api/getUserAccountsByDepCode")
public List<String> getUserAccountsByDepCode(String orgCode){
return sysBaseApi.getUserAccountsByDepCode(orgCode);
}
/**
* sql
*
* @param selectSql
* @return
*/
@GetMapping("/sys/api/dictTableWhiteListCheckBySql")
public boolean dictTableWhiteListCheckBySql(@RequestParam("selectSql") String selectSql) {
return sysBaseApi.dictTableWhiteListCheckBySql(selectSql);
}
/**
*
*
* @param tableOrDictCode dictCode
* @param fields dictCodenull
* @return
*/
@GetMapping("/sys/api/dictTableWhiteListCheckByDict")
public boolean dictTableWhiteListCheckByDict(
@RequestParam("tableOrDictCode") String tableOrDictCode,
@RequestParam(value = "fields", required = false) String[] fields
) {
return sysBaseApi.dictTableWhiteListCheckByDict(tableOrDictCode, fields);
}
}

View File

@ -1,5 +1,6 @@
package org.jeecg.modules.message.enums;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.system.annotation.EnumDict;
import org.jeecg.common.system.vo.DictModel;
@ -15,6 +16,7 @@ import java.util.List;
* @Author taoYan
* @Date 2022/8/19 20:41
**/
@Slf4j
@EnumDict("rangeDate")
public enum RangeDateEnum {
@ -25,6 +27,7 @@ public enum RangeDateEnum {
SZ("sz", "上周"),
BY("by", "本月"),
SY("sy", "上月"),
SEVENDAYS("7day", "7日"),
ZDY("zdy", "自定义日期");
String key;
@ -101,6 +104,10 @@ public enum RangeDateEnum {
//本月第一天减一天
calendar2.set(Calendar.DAY_OF_MONTH, 1);
calendar2.add(Calendar.DAY_OF_MONTH, -1);
} else if (SEVENDAYS.key.equals(key)){
//七日第一天
calendar1.setTime(new Date());
calendar1.add(Calendar.DATE, -7);
}else{
flag = true;
}

View File

@ -23,6 +23,7 @@ import javax.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Set;
/**
* @Description:
@ -58,7 +59,6 @@ public class EmailSendMsgHandle implements ISendMsgHandle {
//update-begin-authortaoyan date:20200811 for:配置类数据获取
if(oConvertUtils.isEmpty(emailFrom)){
StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
log.info("邮件配置 emailFrom" + emailFrom);
setEmailFrom(staticConfig.getEmailFrom());
}
//update-end-authortaoyan date:20200811 for:配置类数据获取
@ -92,6 +92,20 @@ public class EmailSendMsgHandle implements ISendMsgHandle {
log.info("邮件内容:"+ content);
sendMsg(email, title, content);
}
//update-begin-author:taoyan date:2023-6-20 for: QQYUN-5557【简流】通知节点 发送邮箱 表单上有一个邮箱字段,流程中,邮件发送节点,邮件接收人 不可选择邮箱
Set<String> toEmailList = messageDTO.getToEmailList();
if(toEmailList!=null && toEmailList.size()>0){
for(String email: toEmailList){
if (ObjectUtils.isEmpty(email)) {
continue;
}
log.info("邮件内容:"+ content);
sendMsg(email, title, content);
}
}
//update-end-author:taoyan date:2023-6-20 for: QQYUN-5557【简流】通知节点 发送邮箱 表单上有一个邮箱字段,流程中,邮件发送节点,邮件接收人 不可选择邮箱
//发送给抄送人
sendMessageToCopyUser(messageDTO);
}
@ -115,30 +129,56 @@ public class EmailSendMsgHandle implements ISendMsgHandle {
}
content=replaceContent(user,content);
log.info("邮件内容:" + content);
JavaMailSender mailSender = (JavaMailSender) SpringContextUtils.getBean("mailSender");
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = null;
if (oConvertUtils.isEmpty(emailFrom)) {
StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
setEmailFrom(staticConfig.getEmailFrom());
}
try {
helper = new MimeMessageHelper(message, true);
// 设置发送方邮箱地址
helper.setFrom(emailFrom);
helper.setTo(email);
//设置抄送人
helper.setCc(email);
helper.setSubject(title);
helper.setText(content, true);
mailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
//update-begin-author:taoyan date:2023-6-20 for: QQYUN-5557【简流】通知节点 发送邮箱 表单上有一个邮箱字段,流程中,邮件发送节点,邮件接收人 不可选择邮箱
sendEmail(email, content, title);
}
Set<String> ccEmailList = messageDTO.getCcEmailList();
if(ccEmailList!=null && ccEmailList.size()>0){
for(String email: ccEmailList){
if (ObjectUtils.isEmpty(email)) {
continue;
}
log.info("邮件内容:"+ content);
sendEmail(email, content, title);
}
}
}
}
/**
*
* @param email
* @param content
* @param title
*/
private void sendEmail(String email, String content, String title){
JavaMailSender mailSender = (JavaMailSender) SpringContextUtils.getBean("mailSender");
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = null;
if (oConvertUtils.isEmpty(emailFrom)) {
StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
setEmailFrom(staticConfig.getEmailFrom());
}
try {
helper = new MimeMessageHelper(message, true);
// 设置发送方邮箱地址
helper.setFrom(emailFrom);
helper.setTo(email);
//设置抄送人
helper.setCc(email);
helper.setSubject(title);
helper.setText(content, true);
mailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
//update-end-author:taoyan date:2023-6-20 for: QQYUN-5557【简流】通知节点 发送邮箱 表单上有一个邮箱字段,流程中,邮件发送节点,邮件接收人 不可选择邮箱
/**
*
* @param user

View File

@ -8,7 +8,7 @@ import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.message.enums.Vue3MessageHrefEnum;
import org.jeecg.common.constant.enums.Vue3MessageHrefEnum;
import org.jeecg.modules.message.handle.ISendMsgHandle;
import org.jeecg.modules.message.websocket.WebSocket;
import org.jeecg.modules.system.entity.SysAnnouncement;
@ -92,6 +92,12 @@ public class SystemSendMsgHandle implements ISendMsgHandle {
announcement.setBusId(taskId.toString());
announcement.setBusType(Vue3MessageHrefEnum.BPM_TASK.getBusType());
}
// 流程内消息节点 发消息会传一个busType
Object busType = data.get(CommonConstant.NOTICE_MSG_BUS_TYPE);
if(busType!=null){
announcement.setBusType(busType.toString());
}
}
announcement.setTitile(title);
announcement.setMsgContent(msgContent);

View File

@ -22,7 +22,7 @@ public class SocketHandler implements JeecgRedisListener {
@Override
public void onMessage(BaseMap map) {
log.info("【Redis发布订阅模式】redis Listener: {},参数:{}",WebSocket.REDIS_TOPIC_NAME, map.toString());
log.debug("【Redis发布订阅模式】redis Listener: {},参数:{}",WebSocket.REDIS_TOPIC_NAME, map.toString());
String userId = map.get("userId");
String message = map.get("message");

View File

@ -40,7 +40,7 @@ public class WebSocket {
public void onOpen(Session session, @PathParam(value = "userId") String userId) {
try {
sessionPool.put(userId, session);
log.info("【系统 WebSocket】有新的连接总数为:" + sessionPool.size());
log.debug("【系统 WebSocket】有新的连接总数为:" + sessionPool.size());
} catch (Exception e) {
}
}
@ -49,7 +49,7 @@ public class WebSocket {
public void onClose(@PathParam("userId") String userId) {
try {
sessionPool.remove(userId);
log.info("【系统 WebSocket】连接断开总数为:" + sessionPool.size());
log.debug("【系统 WebSocket】连接断开总数为:" + sessionPool.size());
} catch (Exception e) {
e.printStackTrace();
}
@ -70,7 +70,7 @@ public class WebSocket {
try {
//update-begin-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
synchronized (session){
log.info("【系统 WebSocket】推送单人消息:" + message);
log.debug("【系统 WebSocket】推送单人消息:" + message);
session.getBasicRemote().sendText(message);
}
//update-end-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
@ -93,7 +93,7 @@ public class WebSocket {
log.error(e.getMessage(), e);
}
}
log.info("【系统 WebSocket】群发消息:" + message);
log.debug("【系统 WebSocket】群发消息:" + message);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
@ -106,7 +106,7 @@ public class WebSocket {
@OnMessage
public void onMessage(String message, @PathParam(value = "userId") String userId) {
if(!"ping".equals(message) && !WebsocketConst.CMD_CHECK.equals(message)){
log.info("【系统 WebSocket】收到客户端消息:" + message);
log.debug("【系统 WebSocket】收到客户端消息:" + message);
}else{
log.debug("【系统 WebSocket】收到客户端消息:" + message);
}
@ -142,11 +142,11 @@ public class WebSocket {
* @param message
*/
public void sendMessage(String message) {
//log.info("【系统 WebSocket】广播消息:" + message);
//log.debug("【系统 WebSocket】广播消息:" + message);
BaseMap baseMap = new BaseMap();
baseMap.put("userId", "");
baseMap.put("message", message);
jeecgRedisClient.sendMessage(REDIS_TOPIC_NAME, baseMap);
jeecgRedisClient.sendMessage(WebSocket.REDIS_TOPIC_NAME, baseMap);
}
/**
@ -159,7 +159,7 @@ public class WebSocket {
BaseMap baseMap = new BaseMap();
baseMap.put("userId", userId);
baseMap.put("message", message);
jeecgRedisClient.sendMessage(REDIS_TOPIC_NAME, baseMap);
jeecgRedisClient.sendMessage(WebSocket.REDIS_TOPIC_NAME, baseMap);
}
/**

View File

@ -1,37 +1,37 @@
package org.jeecg.modules.ngalain.service;
import com.alibaba.fastjson.JSONArray;
import java.util.List;
import java.util.Map;
/**
* @Description: NgAlainService
* @author: jeecg-boot
*/
public interface NgAlainService {
/**
*
* @param id
* @return JSONArray
* @throws Exception
*/
public JSONArray getMenu(String id) throws Exception;
/**
* jeecg
* @param id
* @return JSONArray
* @throws Exception
*/
public JSONArray getJeecgMenu(String id) throws Exception;
/**
*
* @param table
* @param key
* @param value
* @return List<Map<String, String>>
*/
public List<Map<String, String>> getDictByTable(String table, String key, String value);
}
//package org.jeecg.modules.ngalain.service;
//
//import com.alibaba.fastjson.JSONArray;
//
//import java.util.List;
//import java.util.Map;
//
///**
// * @Description: NgAlainService接口
// * @author: jeecg-boot
// */
//public interface NgAlainService {
// /**
// * 菜单
// * @param id
// * @return JSONArray
// * @throws Exception
// */
// public JSONArray getMenu(String id) throws Exception;
//
// /**
// * jeecg菜单
// * @param id
// * @return JSONArray
// * @throws Exception
// */
// public JSONArray getJeecgMenu(String id) throws Exception;
//
// /**
// * 获取字典值
// * @param table
// * @param key
// * @param value
// * @return List<Map<String, String>>
// */
// public List<Map<String, String>> getDictByTable(String table, String key, String value);
//}

View File

@ -1,187 +1,187 @@
package org.jeecg.modules.ngalain.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.ngalain.service.NgAlainService;
import org.jeecg.modules.system.entity.SysPermission;
import org.jeecg.modules.system.mapper.SysDictMapper;
import org.jeecg.modules.system.service.ISysPermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Base64;
import java.util.List;
import java.util.Map;
/**
* @Description: NgAlainServiceImpl
* @author: jeecg-boot
*/
@Service("ngAlainService")
public class NgAlainServiceImpl implements NgAlainService {
@Autowired
private ISysPermissionService sysPermissionService;
@Autowired
private SysDictMapper mapper;
@Override
public JSONArray getMenu(String id) throws Exception {
return getJeecgMenu(id);
}
@Override
public JSONArray getJeecgMenu(String id) throws Exception {
List<SysPermission> metaList = sysPermissionService.queryByUser(id);
JSONArray jsonArray = new JSONArray();
getPermissionJsonArray(jsonArray, metaList, null);
JSONArray menulist= parseNgAlain(jsonArray);
JSONObject jeecgMenu = new JSONObject();
jeecgMenu.put("text", "jeecg菜单");
jeecgMenu.put("group",true);
jeecgMenu.put("children", menulist);
JSONArray jeecgMenuList=new JSONArray();
jeecgMenuList.add(jeecgMenu);
return jeecgMenuList;
}
@Override
public List<Map<String, String>> getDictByTable(String table, String key, String value) {
return this.mapper.getDictByTableNgAlain(table,key,value);
}
private JSONArray parseNgAlain(JSONArray jsonArray) {
JSONArray menulist=new JSONArray();
for (Object object : jsonArray) {
JSONObject jsonObject= (JSONObject) object;
String path= (String) jsonObject.get("path");
JSONObject meta= (JSONObject) jsonObject.get("meta");
JSONObject menu=new JSONObject();
menu.put("text",meta.get("title"));
menu.put("reuse",true);
if (jsonObject.get("children")!=null){
JSONArray child= parseNgAlain((JSONArray) jsonObject.get("children"));
menu.put("children",child);
JSONObject icon=new JSONObject();
icon.put("type", "icon");
icon.put("value", meta.get("icon"));
menu.put("icon",icon);
}else {
menu.put("link",path);
}
menulist.add(menu);
}
return menulist;
}
/**
* JSON
* @param jsonArray
* @param metaList
* @param parentJson
*/
private void getPermissionJsonArray(JSONArray jsonArray,List<SysPermission> metaList,JSONObject parentJson) {
for (SysPermission permission : metaList) {
if(permission.getMenuType()==null) {
continue;
}
String tempPid = permission.getParentId();
JSONObject json = getPermissionJsonObject(permission);
if(parentJson==null && oConvertUtils.isEmpty(tempPid)) {
jsonArray.add(json);
if(!permission.isLeaf()) {
getPermissionJsonArray(jsonArray, metaList, json);
}
}else if(parentJson!=null && oConvertUtils.isNotEmpty(tempPid) && tempPid.equals(parentJson.getString("id"))){
if(permission.getMenuType()==0) {
JSONObject metaJson = parentJson.getJSONObject("meta");
if(metaJson.containsKey("permissionList")) {
metaJson.getJSONArray("permissionList").add(json);
}else {
JSONArray permissionList = new JSONArray();
permissionList.add(json);
metaJson.put("permissionList", permissionList);
}
}else if(permission.getMenuType()==1) {
if(parentJson.containsKey("children")) {
parentJson.getJSONArray("children").add(json);
}else {
JSONArray children = new JSONArray();
children.add(json);
parentJson.put("children", children);
}
if(!permission.isLeaf()) {
getPermissionJsonArray(jsonArray, metaList, json);
}
}
}
}
}
private JSONObject getPermissionJsonObject(SysPermission permission) {
JSONObject json = new JSONObject();
//类型(0一级菜单 1子菜单 2按钮)
if(CommonConstant.MENU_TYPE_2.equals(permission.getMenuType())) {
json.put("action", permission.getPerms());
json.put("describe", permission.getName());
}else if(CommonConstant.MENU_TYPE_0.equals(permission.getMenuType()) || CommonConstant.MENU_TYPE_1.equals(permission.getMenuType())) {
json.put("id", permission.getId());
boolean flag = permission.getUrl()!=null&&(permission.getUrl().startsWith(CommonConstant.HTTP_PROTOCOL)||permission.getUrl().startsWith(CommonConstant.HTTPS_PROTOCOL));
if(flag) {
String url= new String(Base64.getUrlEncoder().encode(permission.getUrl().getBytes()));
json.put("path", "/sys/link/" +url.replaceAll("=",""));
}else {
json.put("path", permission.getUrl());
}
//重要规则路由name (通过URL生成路由name,路由name供前端开发页面跳转使用)
json.put("name", urlToRouteName(permission.getUrl()));
//是否隐藏路由,默认都是显示的
if(permission.isHidden()) {
json.put("hidden",true);
}
//聚合路由
if(permission.isAlwaysShow()) {
json.put("alwaysShow",true);
}
json.put("component", permission.getComponent());
JSONObject meta = new JSONObject();
meta.put("title", permission.getName());
if(oConvertUtils.isEmpty(permission.getParentId())) {
//一级菜单跳转地址
json.put("redirect",permission.getRedirect());
meta.put("icon", oConvertUtils.getString(permission.getIcon(), ""));
}else {
meta.put("icon", oConvertUtils.getString(permission.getIcon(), ""));
}
if(flag) {
meta.put("url", permission.getUrl());
}
json.put("meta", meta);
}
return json;
}
/**
* URLnameURL/-
* URL = /isystem/role
* RouteName = isystem-role
* @return
*/
private String urlToRouteName(String url) {
if(oConvertUtils.isNotEmpty(url)) {
if(url.startsWith(SymbolConstant.SINGLE_SLASH)) {
url = url.substring(1);
}
url = url.replace("/", "-");
return url;
}else {
return null;
}
}
}
//package org.jeecg.modules.ngalain.service.impl;
//
//import com.alibaba.fastjson.JSONArray;
//import com.alibaba.fastjson.JSONObject;
//import org.jeecg.common.constant.CommonConstant;
//import org.jeecg.common.constant.SymbolConstant;
//import org.jeecg.common.util.oConvertUtils;
//import org.jeecg.modules.ngalain.service.NgAlainService;
//import org.jeecg.modules.system.entity.SysPermission;
//import org.jeecg.modules.system.mapper.SysDictMapper;
//import org.jeecg.modules.system.service.ISysPermissionService;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import java.util.Base64;
//import java.util.List;
//import java.util.Map;
//
///**
// * @Description: NgAlainServiceImpl 实现类
// * @author: jeecg-boot
// */
//@Service("ngAlainService")
//public class NgAlainServiceImpl implements NgAlainService {
// @Autowired
// private ISysPermissionService sysPermissionService;
// @Autowired
// private SysDictMapper mapper;
// @Override
// public JSONArray getMenu(String id) throws Exception {
// return getJeecgMenu(id);
// }
// @Override
// public JSONArray getJeecgMenu(String id) throws Exception {
// List<SysPermission> metaList = sysPermissionService.queryByUser(id);
// JSONArray jsonArray = new JSONArray();
// getPermissionJsonArray(jsonArray, metaList, null);
// JSONArray menulist= parseNgAlain(jsonArray);
// JSONObject jeecgMenu = new JSONObject();
// jeecgMenu.put("text", "jeecg菜单");
// jeecgMenu.put("group",true);
// jeecgMenu.put("children", menulist);
// JSONArray jeecgMenuList=new JSONArray();
// jeecgMenuList.add(jeecgMenu);
// return jeecgMenuList;
// }
//
// @Override
// public List<Map<String, String>> getDictByTable(String table, String key, String value) {
// return this.mapper.getDictByTableNgAlain(table,key,value);
// }
//
// private JSONArray parseNgAlain(JSONArray jsonArray) {
// JSONArray menulist=new JSONArray();
// for (Object object : jsonArray) {
// JSONObject jsonObject= (JSONObject) object;
// String path= (String) jsonObject.get("path");
// JSONObject meta= (JSONObject) jsonObject.get("meta");
// JSONObject menu=new JSONObject();
// menu.put("text",meta.get("title"));
// menu.put("reuse",true);
// if (jsonObject.get("children")!=null){
// JSONArray child= parseNgAlain((JSONArray) jsonObject.get("children"));
// menu.put("children",child);
// JSONObject icon=new JSONObject();
// icon.put("type", "icon");
// icon.put("value", meta.get("icon"));
// menu.put("icon",icon);
// }else {
// menu.put("link",path);
// }
// menulist.add(menu);
// }
// return menulist;
// }
//
// /**
// * 获取菜单JSON数组
// * @param jsonArray
// * @param metaList
// * @param parentJson
// */
// private void getPermissionJsonArray(JSONArray jsonArray,List<SysPermission> metaList,JSONObject parentJson) {
// for (SysPermission permission : metaList) {
// if(permission.getMenuType()==null) {
// continue;
// }
// String tempPid = permission.getParentId();
// JSONObject json = getPermissionJsonObject(permission);
// if(parentJson==null && oConvertUtils.isEmpty(tempPid)) {
// jsonArray.add(json);
// if(!permission.isLeaf()) {
// getPermissionJsonArray(jsonArray, metaList, json);
// }
// }else if(parentJson!=null && oConvertUtils.isNotEmpty(tempPid) && tempPid.equals(parentJson.getString("id"))){
// if(permission.getMenuType()==0) {
// JSONObject metaJson = parentJson.getJSONObject("meta");
// if(metaJson.containsKey("permissionList")) {
// metaJson.getJSONArray("permissionList").add(json);
// }else {
// JSONArray permissionList = new JSONArray();
// permissionList.add(json);
// metaJson.put("permissionList", permissionList);
// }
//
// }else if(permission.getMenuType()==1) {
// if(parentJson.containsKey("children")) {
// parentJson.getJSONArray("children").add(json);
// }else {
// JSONArray children = new JSONArray();
// children.add(json);
// parentJson.put("children", children);
// }
//
// if(!permission.isLeaf()) {
// getPermissionJsonArray(jsonArray, metaList, json);
// }
// }
// }
//
//
// }
// }
// private JSONObject getPermissionJsonObject(SysPermission permission) {
// JSONObject json = new JSONObject();
// //类型(0一级菜单 1子菜单 2按钮)
// if(CommonConstant.MENU_TYPE_2.equals(permission.getMenuType())) {
// json.put("action", permission.getPerms());
// json.put("describe", permission.getName());
// }else if(CommonConstant.MENU_TYPE_0.equals(permission.getMenuType()) || CommonConstant.MENU_TYPE_1.equals(permission.getMenuType())) {
// json.put("id", permission.getId());
// boolean flag = permission.getUrl()!=null&&(permission.getUrl().startsWith(CommonConstant.HTTP_PROTOCOL)||permission.getUrl().startsWith(CommonConstant.HTTPS_PROTOCOL));
// if(flag) {
// String url= new String(Base64.getUrlEncoder().encode(permission.getUrl().getBytes()));
// json.put("path", "/sys/link/" +url.replaceAll("=",""));
// }else {
// json.put("path", permission.getUrl());
// }
//
// //重要规则路由name (通过URL生成路由name,路由name供前端开发页面跳转使用)
// json.put("name", urlToRouteName(permission.getUrl()));
//
// //是否隐藏路由,默认都是显示的
// if(permission.isHidden()) {
// json.put("hidden",true);
// }
// //聚合路由
// if(permission.isAlwaysShow()) {
// json.put("alwaysShow",true);
// }
// json.put("component", permission.getComponent());
// JSONObject meta = new JSONObject();
// meta.put("title", permission.getName());
// if(oConvertUtils.isEmpty(permission.getParentId())) {
// //一级菜单跳转地址
// json.put("redirect",permission.getRedirect());
// meta.put("icon", oConvertUtils.getString(permission.getIcon(), ""));
// }else {
// meta.put("icon", oConvertUtils.getString(permission.getIcon(), ""));
// }
// if(flag) {
// meta.put("url", permission.getUrl());
// }
// json.put("meta", meta);
// }
//
// return json;
// }
// /**
// * 通过URL生成路由name去掉URL前缀斜杠替换内容中的斜杠/’为-
// * 举例: URL = /isystem/role
// * RouteName = isystem-role
// * @return
// */
// private String urlToRouteName(String url) {
// if(oConvertUtils.isNotEmpty(url)) {
// if(url.startsWith(SymbolConstant.SINGLE_SLASH)) {
// url = url.substring(1);
// }
// url = url.replace("/", "-");
// return url;
// }else {
// return null;
// }
// }
//}

View File

@ -1,22 +1,17 @@
package org.jeecg.modules.system.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.enums.FileTypeEnum;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.RestUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.filter.FileTypeFilter;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.system.service.ISysFilesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
@ -28,7 +23,7 @@ import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
/**
* <p>
*
@ -95,7 +90,7 @@ public class CommonController {
}
if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
//update-begin-author:liusq date:20221102 for: 过滤上传文件类型
FileTypeFilter.fileTypeFilter(file);
SsrfFileTypeFilter.checkUploadFileType(file);
//update-end-author:liusq date:20221102 for: 过滤上传文件类型
//update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传
savePath = this.uploadLocal(file,bizPath);
@ -227,11 +222,17 @@ public class CommonController {
if (imgPath.endsWith(SymbolConstant.COMMA)) {
imgPath = imgPath.substring(0, imgPath.length() - 1);
}
//update-begin---author:liusq ---date:20230912 for检查下载文件类型--------------
SsrfFileTypeFilter.checkDownloadFileType(imgPath);
//update-end---author:liusq ---date:20230912 for检查下载文件类型--------------
String filePath = uploadpath + File.separator + imgPath;
File file = new File(filePath);
if(!file.exists()){
response.setStatus(404);
throw new RuntimeException("文件["+imgPath+"]不存在..");
log.error("文件["+imgPath+"]不存在..");
return;
//throw new RuntimeException();
}
// 设置强制下载不打开
response.setContentType("application/force-download");

View File

@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletRequest;
public class DuplicateCheckController {
@Autowired
ISysDictService sysDictService;
ISysDictService sysDictService;
/**
*
@ -59,5 +59,6 @@ public class DuplicateCheckController {
return Result.error("该值不可用,系统中已存在!");
}
}
}

View File

@ -14,6 +14,7 @@ import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.enums.DySmsEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.*;
@ -37,6 +38,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author scott
@ -60,12 +62,9 @@ public class LoginController {
@Autowired
private ISysDepartService sysDepartService;
@Autowired
private ISysTenantService sysTenantService;
@Autowired
private ISysDictService sysDictService;
@Resource
private BaseCommonService baseCommonService;
@Autowired
private JeecgBaseConfig jeecgBaseConfig;

View File

@ -41,6 +41,7 @@ import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@ -51,6 +52,10 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.jeecg.common.constant.CommonConstant.ANNOUNCEMENT_SEND_STATUS_1;
@ -82,6 +87,12 @@ public class SysAnnouncementController {
@Lazy
private RedisUtil redisUtil;
/**
* QQYUN-5072线
*/
public static ExecutorService cachedThreadPool = new ThreadPoolExecutor(0, 1024,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
public static ExecutorService completeNoteThreadPool = new ThreadPoolExecutor(0, 1024,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
/**
*
* @param sysAnnouncement
@ -105,19 +116,6 @@ public class SysAnnouncementController {
sysAnnouncement.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
QueryWrapper<SysAnnouncement> queryWrapper = QueryGenerator.initQueryWrapper(sysAnnouncement, req.getParameterMap());
Page<SysAnnouncement> page = new Page<SysAnnouncement>(pageNo,pageSize);
//update-begin-author:lvdandan date:20211229 for: sqlserver mssql-jdbc 8.2.2.jre8版本下系统公告列表查询报错 查询SQL中生成了两个create_time DESC故注释此段代码
//排序逻辑 处理
// String column = req.getParameter("column");
// String order = req.getParameter("order");
// if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
// if("asc".equals(order)) {
// queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
// }else {
// queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
// }
// }
//update-end-author:lvdandan date:20211229 for: sqlserver mssql-jdbc 8.2.2.jre8版本下系统公告列表查询报错 查询SQL中生成了两个create_time DESC故注释此段代码
IPage<SysAnnouncement> pageList = sysAnnouncementService.page(page, queryWrapper);
result.setSuccess(true);
result.setResult(pageList);
@ -256,8 +254,12 @@ public class SysAnnouncementController {
sysAnnouncement.setSender(currentUserName);
boolean ok = sysAnnouncementService.updateById(sysAnnouncement);
if(ok) {
result.success("该系统通知发布成功");
result.success("系统通知推送成功");
if(sysAnnouncement.getMsgType().equals(CommonConstant.MSG_TYPE_ALL)) {
// 补全公告和用户之前的关系
sysAnnouncementService.batchInsertSysAnnouncementSend(sysAnnouncement.getId(), sysAnnouncement.getTenantId());
// 推送websocket通知
JSONObject obj = new JSONObject();
obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_TOPIC);
obj.put(WebsocketConst.MSG_ID, sysAnnouncement.getId());
@ -277,7 +279,7 @@ public class SysAnnouncementController {
}
try {
// 同步企业微信、钉钉的消息通知
Response<String> dtResponse = dingtalkService.sendActionCardMessage(sysAnnouncement, true);
Response<String> dtResponse = dingtalkService.sendActionCardMessage(sysAnnouncement, null, true);
wechatEnterpriseService.sendTextCardMessage(sysAnnouncement, true);
if (dtResponse != null && dtResponse.isSuccess()) {
@ -332,54 +334,28 @@ public class SysAnnouncementController {
@RequestMapping(value = "/listByUser", method = RequestMethod.GET)
public Result<Map<String, Object>> listByUser(@RequestParam(required = false, defaultValue = "5") Integer pageSize) {
Result<Map<String,Object>> result = new Result<Map<String,Object>>();
Map<String,Object> sysMsgMap = new HashMap(5);
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
// 1.将系统消息补充到用户通告阅读标记表中
LambdaQueryWrapper<SysAnnouncement> querySaWrapper = new LambdaQueryWrapper<SysAnnouncement>();
//全部人员
querySaWrapper.eq(SysAnnouncement::getMsgType,CommonConstant.MSG_TYPE_ALL);
//未删除
querySaWrapper.eq(SysAnnouncement::getDelFlag,CommonConstant.DEL_FLAG_0.toString());
//已发布
querySaWrapper.eq(SysAnnouncement::getSendStatus, CommonConstant.HAS_SEND);
//新注册用户不看结束通知
querySaWrapper.ge(SysAnnouncement::getEndTime, sysUser.getCreateTime());
//update-begin--Author:liusq Date:20210108 for[JT-424] 【开源issue】bug处理--------------------
querySaWrapper.notInSql(SysAnnouncement::getId,"select annt_id from sys_announcement_send where user_id='"+userId+"'");
//update-begin--Author:liusq Date:20210108 for [JT-424] 【开源issue】bug处理--------------------
List<SysAnnouncement> announcements = sysAnnouncementService.list(querySaWrapper);
if(announcements.size()>0) {
for(int i=0;i<announcements.size();i++) {
//update-begin--Author:wangshuai Date:20200803 for 通知公告消息重复LOWCOD-759--------------------
//因为websocket没有判断是否存在这个用户要是判断会出现问题故在此判断逻辑
LambdaQueryWrapper<SysAnnouncementSend> query = new LambdaQueryWrapper<>();
query.eq(SysAnnouncementSend::getAnntId,announcements.get(i).getId());
query.eq(SysAnnouncementSend::getUserId,userId);
SysAnnouncementSend one = sysAnnouncementSendService.getOne(query);
if(null==one){
log.info("listByUser接口新增了SysAnnouncementSendpageSize{}"+pageSize);
SysAnnouncementSend announcementSend = new SysAnnouncementSend();
announcementSend.setAnntId(announcements.get(i).getId());
announcementSend.setUserId(userId);
announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
sysAnnouncementSendService.save(announcementSend);
log.info("announcementSend.toString()",announcementSend.toString());
}
//update-end--Author:wangshuai Date:20200803 for 通知公告消息重复LOWCOD-759------------
}
}
// //补推送数据(用户和通知的关系表)
// completeNoteThreadPool.execute(()->{
// sysAnnouncementService.completeAnnouncementSendInfo();
// });
// 2.查询用户未读的系统消息
Page<SysAnnouncement> anntMsgList = new Page<SysAnnouncement>(0, pageSize);
//通知公告消息
anntMsgList = sysAnnouncementService.querySysCementPageByUserId(anntMsgList,userId,"1");
Page<SysAnnouncement> sysMsgList = new Page<SysAnnouncement>(0, pageSize);
//系统消息
sysMsgList = sysAnnouncementService.querySysCementPageByUserId(sysMsgList,userId,"2");
Map<String,Object> sysMsgMap = new HashMap(5);
sysMsgMap.put("sysMsgList", sysMsgList.getRecords());
sysMsgMap.put("sysMsgTotal", sysMsgList.getTotal());
sysMsgMap.put("anntMsgList", anntMsgList.getRecords());
sysMsgMap.put("anntMsgTotal", anntMsgList.getTotal());
//系统消息
Page<SysAnnouncement> sysMsgList = new Page<SysAnnouncement>(0, pageSize);
sysMsgList = sysAnnouncementService.querySysCementPageByUserId(sysMsgList,userId,"2");
sysMsgMap.put("sysMsgList", sysMsgList.getRecords());
sysMsgMap.put("sysMsgTotal", sysMsgList.getTotal());
result.setSuccess(true);
result.setResult(sysMsgMap);
return result;
@ -528,39 +504,56 @@ public class SysAnnouncementController {
public Result<List<SysAnnouncement>> vue3List(@RequestParam(name="fromUser", required = false) String fromUser,
@RequestParam(name="starFlag", required = false) String starFlag,
@RequestParam(name="rangeDateKey", required = false) String rangeDateKey,
@RequestParam(name="beginDate", required = false) String beginDate, @RequestParam(name="endDate", required = false) String endDate,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo, @RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
// 后台获取开始时间/结束时间
Date bd=null, ed=null;
if(RangeDateEnum.ZDY.getKey().equals(rangeDateKey)){
if(oConvertUtils.isNotEmpty(beginDate)){
bd = DateUtils.parseDatetime(beginDate);
@RequestParam(name="beginDate", required = false) String beginDate,
@RequestParam(name="endDate", required = false) String endDate,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
long calStartTime = System.currentTimeMillis(); // 记录开始时间
// 1、获取日期查询条件开始时间和结束时间
Date beginTime = null, endTime = null;
if (RangeDateEnum.ZDY.getKey().equals(rangeDateKey)) {
// 自定义日期范围查询
if (oConvertUtils.isNotEmpty(beginDate)) {
beginTime = DateUtils.parseDatetime(beginDate);
}
if(oConvertUtils.isNotEmpty(endDate)){
ed = DateUtils.parseDatetime(endDate);
if (oConvertUtils.isNotEmpty(endDate)) {
endTime = DateUtils.parseDatetime(endDate);
}
}else{
} else {
// 日期段落查询
Date[] arr = RangeDateEnum.getRangeArray(rangeDateKey);
if(arr!=null){
bd = arr[0];
ed = arr[1];
if (arr != null) {
beginTime = arr[0];
endTime = arr[1];
}
}
List<SysAnnouncement> ls = this.sysAnnouncementService.querySysMessageList(pageSize, pageNo, fromUser, starFlag, bd, ed);
//查询出来的消息全部设置为已读
if(ls!=null && ls.size()>0){
// 2、根据条件查询用户的通知消息
List<SysAnnouncement> ls = this.sysAnnouncementService.querySysMessageList(pageSize, pageNo, fromUser, starFlag, beginTime, endTime);
// 3、设置当前页的消息为已读
if (!CollectionUtils.isEmpty(ls)) {
// 设置已读
String readed = "1";
List<String> annoceIdList = ls.stream().filter(item->!readed.equals(item.getReadFlag())).map(item->item.getId()).collect(Collectors.toList());
if(annoceIdList!=null && annoceIdList.size()>0){
sysAnnouncementService.updateReaded(annoceIdList);
List<String> annoceIdList = ls.stream().filter(item -> !readed.equals(item.getReadFlag())).map(item -> item.getId()).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(annoceIdList)) {
cachedThreadPool.execute(() -> {
sysAnnouncementService.updateReaded(annoceIdList);
});
}
}
//update-begin-author:taoyan date:2022-9-25 for: VUEN-2261【移动端 系统消息】通知公告显示7条消息点进去查看后仍然显示7条其他地方已读后未读条数减少
JSONObject obj = new JSONObject();
obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_USER);
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
webSocket.sendMessage(sysUser.getId(), obj.toJSONString());
//update-end-author:taoyan date:2022-9-25 for: VUEN-2261【移动端 系统消息】通知公告显示7条消息点进去查看后仍然显示7条其他地方已读后未读条数减少
// 4、性能统计耗时
long calEndTime = System.currentTimeMillis(); // 记录结束时间
long duration = calEndTime - calStartTime; // 计算耗时
System.out.println("耗时:" + duration + " 毫秒");
return Result.ok(ls);
}
@ -583,4 +576,14 @@ public class SysAnnouncementController {
result.setResult(pageList);
return result;
}
/**
*
* @return
*/
@PostMapping("/clearAllUnReadMessage")
public Result<String> clearAllUnReadMessage(){
sysAnnouncementService.clearAllUnReadMessage();
return Result.ok("清除未读消息成功");
}
}

View File

@ -72,15 +72,11 @@ public class SysAnnouncementSendController {
String column = req.getParameter("column");
String order = req.getParameter("order");
//issues/3331 SQL injection vulnerability
SqlInjectionUtil.filterContent(column);
SqlInjectionUtil.filterContent(order);
if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
if(DataBaseConstant.SQL_ASC.equals(order)) {
queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
queryWrapper.orderByAsc(SqlInjectionUtil.getSqlInjectSortField(column));
}else {
queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
queryWrapper.orderByDesc(SqlInjectionUtil.getSqlInjectSortField(column));
}
}
IPage<SysAnnouncementSend> pageList = sysAnnouncementSendService.page(page, queryWrapper);
@ -203,7 +199,11 @@ public class SysAnnouncementSendController {
LambdaUpdateWrapper<SysAnnouncementSend> updateWrapper = new UpdateWrapper().lambda();
updateWrapper.set(SysAnnouncementSend::getReadFlag, CommonConstant.HAS_READ_FLAG);
updateWrapper.set(SysAnnouncementSend::getReadTime, new Date());
updateWrapper.last("where annt_id ='"+anntId+"' and user_id ='"+userId+"'");
//update-begin-author:liusq date:2023-09-04 for:系统模块存在的sql漏洞写法
updateWrapper.eq(SysAnnouncementSend::getAnntId,anntId);
updateWrapper.eq(SysAnnouncementSend::getUserId,userId);
//update-end-author:liusq date:2023-09-04 for: 系统模块存在的sql漏洞写法
//updateWrapper.last("where annt_id ='"+anntId+"' and user_id ='"+userId+"'");
SysAnnouncementSend announcementSend = new SysAnnouncementSend();
sysAnnouncementSendService.update(announcementSend, updateWrapper);
result.setSuccess(true);
@ -243,7 +243,8 @@ public class SysAnnouncementSendController {
LambdaUpdateWrapper<SysAnnouncementSend> updateWrapper = new UpdateWrapper().lambda();
updateWrapper.set(SysAnnouncementSend::getReadFlag, CommonConstant.HAS_READ_FLAG);
updateWrapper.set(SysAnnouncementSend::getReadTime, new Date());
updateWrapper.last("where user_id ='"+userId+"'");
updateWrapper.eq(SysAnnouncementSend::getUserId,userId);
//updateWrapper.last("where user_id ='"+userId+"'");
SysAnnouncementSend announcementSend = new SysAnnouncementSend();
sysAnnouncementSendService.update(announcementSend, updateWrapper);
JSONObject socketParams = new JSONObject();

View File

@ -15,7 +15,7 @@ import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.ImportExcelUtil;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.ReflectHelper;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.system.entity.SysCategory;
@ -322,7 +322,7 @@ public class SysCategoryController {
Result<SysCategory> result = new Result<SysCategory>();
try {
//update-begin-author:taoyan date:2022-5-6 for: issues/3663 sql注入问题
boolean isClassField = SqlInjectionUtil.isClassField(field, SysCategory.class);
boolean isClassField = ReflectHelper.isClassField(field, SysCategory.class);
if (!isClassField) {
return Result.error("字段无效,请检查!");
}

View File

@ -102,6 +102,23 @@ public class SysCommentController extends JeecgController<SysComment, ISysCommen
}
}
/**
* app
* @param request
* @return
*/
@ApiOperation(value = "系统评论表-添加文件", notes = "系统评论表-添加文件")
@PostMapping(value = "/appAddFile")
public Result<String> appAddFile(HttpServletRequest request) {
try {
sysCommentService.appSaveOneFileComment(request);
return Result.OK("success");
} catch (Exception e) {
log.error("评论文件上传失败:{}", e.getMessage());
return Result.error("操作失败," + e.getMessage());
}
}
@ApiOperation(value = "系统评论回复表-通过id删除", notes = "系统评论回复表-通过id删除")
@DeleteMapping(value = "/deleteOne")
public Result<String> deleteOne(@RequestParam(name = "id", required = true) String id) {

View File

@ -3,6 +3,7 @@ package org.jeecg.modules.system.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -11,6 +12,7 @@ import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.config.TenantContext;
@ -225,4 +227,6 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
return super.importExcel(request, response, SysDataSource.class);
}
}

View File

@ -25,6 +25,7 @@ import org.jeecg.modules.system.model.SysDepartTreeModel;
import org.jeecg.modules.system.service.ISysDepartService;
import org.jeecg.modules.system.service.ISysUserDepartService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.vo.lowapp.ExportDepartVo;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
@ -137,6 +138,8 @@ public class SysDepartController {
result.setSuccess(true);
} catch (Exception e) {
log.error(e.getMessage(),e);
result.setSuccess(false);
result.setMessage("查询失败");
}
return result;
}
@ -415,6 +418,8 @@ public class SysDepartController {
SysDepart parentDept = sysDepartService.getOne(queryWrapper);
if(!parentDept.equals(null)) {
sysDepart.setParentId(parentDept.getId());
//更新父级部门不是叶子结点
sysDepartService.updateIzLeaf(parentDept.getId(),CommonConstant.NOT_LEAF);
} else {
sysDepart.setParentId("");
}
@ -574,4 +579,80 @@ public class SysDepartController {
}
return result;
}
/**
* idid :
* @param departId
* @return
*/
@GetMapping("/getUsersByDepartTenantId")
public Result<List<SysUser>> getUsersByDepartTenantId(@RequestParam("departId") String departId){
int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
List<SysUser> sysUserList = sysUserDepartService.getUsersByDepartTenantId(departId,tenantId);
return Result.ok(sysUserList);
}
/**
* excel:
*
* @param request
*/
@RequestMapping(value = "/appExportXls")
public ModelAndView appExportXls(SysDepart sysDepart,HttpServletRequest request) {
// Step.1 组装查询条件
int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
List<ExportDepartVo> pageList = sysDepartService.getExcelDepart(tenantId);
//Step.2 AutoPoi 导出Excel
//导出文件名称
mv.addObject(NormalExcelConstants.FILE_NAME, "部门列表");
mv.addObject(NormalExcelConstants.CLASS, ExportDepartVo.class);
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("部门列表数据", "导出人:"+user.getRealname(), "导出信息"));
mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
return mv;
}
/**
* excel:
*
* @param request
*/
@RequestMapping(value = "/appImportExcel", method = RequestMethod.POST)
@CacheEvict(value= {CacheConstant.SYS_DEPARTS_CACHE,CacheConstant.SYS_DEPART_IDS_CACHE}, allEntries=true)
public Result<?> appImportExcel(HttpServletRequest request, HttpServletResponse response) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
List<String> errorMessageList = new ArrayList<>();
List<ExportDepartVo> listSysDeparts = null;
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
// 获取上传文件对象
MultipartFile file = entity.getValue();
ImportParams params = new ImportParams();
params.setTitleRows(2);
params.setHeadRows(1);
params.setNeedSave(true);
try {
listSysDeparts = ExcelImportUtil.importExcel(file.getInputStream(), ExportDepartVo.class, params);
sysDepartService.importExcel(listSysDeparts,errorMessageList);
//清空部门缓存
Set keys3 = redisTemplate.keys(CacheConstant.SYS_DEPARTS_CACHE + "*");
Set keys4 = redisTemplate.keys(CacheConstant.SYS_DEPART_IDS_CACHE + "*");
redisTemplate.delete(keys3);
redisTemplate.delete(keys4);
return ImportExcelUtil.imporReturnRes(errorMessageList.size(), listSysDeparts.size() - errorMessageList.size(), errorMessageList);
} catch (Exception e) {
log.error(e.getMessage(),e);
return Result.error("文件导入失败:"+e.getMessage());
} finally {
try {
file.getInputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return Result.error("文件导入失败!");
}
}

Some files were not shown because too many files have changed in this diff Show More