Commit c925b3d1e8967189542f467e12357d9526e5fa14

Authored by zhangdaiscott
1 parent 940773b4

JeecgBoot 3.3.0 版本发布(发布日期:20220725)

Showing 40 changed files with 1023 additions and 200 deletions
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/MessageDTO.java
... ... @@ -4,6 +4,7 @@ import lombok.Data;
4 4 import org.jeecg.common.constant.CommonConstant;
5 5  
6 6 import java.io.Serializable;
  7 +import java.util.Map;
7 8  
8 9 /**
9 10 * 普通消息
... ... @@ -72,4 +73,18 @@ public class MessageDTO implements Serializable {
72 73 this.category = category;
73 74 }
74 75  
  76 + /**
  77 + * 模板消息对应的模板编码
  78 + */
  79 + protected String templateCode;
  80 + /**
  81 + * 消息类型:org.jeecg.common.constant.enums.MessageTypeEnum
  82 + */
  83 + protected String type;
  84 +
  85 + /**
  86 + * 解析模板内容 对应的数据
  87 + */
  88 + protected Map<String, Object> data;
  89 +
75 90 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/PermissionDataAspect.java
... ... @@ -40,6 +40,8 @@ public class PermissionDataAspect {
40 40 @Autowired
41 41 private CommonAPI commonApi;
42 42  
  43 + private static final String SPOT_DO = ".do";
  44 +
43 45 @Pointcut("@annotation(org.jeecg.common.aspect.annotation.PermissionData)")
44 46 public void pointCut() {
45 47  
... ... @@ -113,7 +115,7 @@ public class PermissionDataAspect {
113 115 requestPath = requestPath.substring(0, requestPath.indexOf("&"));
114 116 }
115 117 if(requestPath.indexOf(QueryRuleEnum.EQ.getValue())!=-1){
116   - if(requestPath.indexOf(CommonConstant.SPOT_DO)!=-1){
  118 + if(requestPath.indexOf(SPOT_DO)!=-1){
117 119 requestPath = requestPath.substring(0,requestPath.indexOf(".do")+3);
118 120 }else{
119 121 requestPath = requestPath.substring(0,requestPath.indexOf("?"));
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DynamicTable.java 0 → 100644
  1 +package org.jeecg.common.aspect.annotation;
  2 +
  3 +import java.lang.annotation.*;
  4 +
  5 +/**
  6 + * 动态table切换
  7 + *
  8 + * @author :zyf
  9 + * @date:2020-04-25
  10 + */
  11 +@Target(ElementType.METHOD)
  12 +@Retention(RetentionPolicy.RUNTIME)
  13 +@Documented
  14 +public @interface DynamicTable {
  15 + /**
  16 + * 需要动态解析的表名
  17 + * @return
  18 + */
  19 + String value();
  20 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java
... ... @@ -9,320 +9,321 @@ public interface CommonConstant {
9 9 /**
10 10 * 正常状态
11 11 */
12   - public static final Integer STATUS_NORMAL = 0;
  12 + Integer STATUS_NORMAL = 0;
13 13  
14 14 /**
15 15 * 禁用状态
16 16 */
17   - public static final Integer STATUS_DISABLE = -1;
  17 + Integer STATUS_DISABLE = -1;
18 18  
19 19 /**
20 20 * 删除标志
21 21 */
22   - public static final Integer DEL_FLAG_1 = 1;
  22 + Integer DEL_FLAG_1 = 1;
23 23  
24 24 /**
25 25 * 未删除
26 26 */
27   - public static final Integer DEL_FLAG_0 = 0;
  27 + Integer DEL_FLAG_0 = 0;
28 28  
29 29 /**
30 30 * 系统日志类型: 登录
31 31 */
32   - public static final int LOG_TYPE_1 = 1;
  32 + int LOG_TYPE_1 = 1;
33 33  
34 34 /**
35 35 * 系统日志类型: 操作
36 36 */
37   - public static final int LOG_TYPE_2 = 2;
  37 + int LOG_TYPE_2 = 2;
38 38  
39 39 /**
40 40 * 操作日志类型: 查询
41 41 */
42   - public static final int OPERATE_TYPE_1 = 1;
  42 + int OPERATE_TYPE_1 = 1;
43 43  
44 44 /**
45 45 * 操作日志类型: 添加
46 46 */
47   - public static final int OPERATE_TYPE_2 = 2;
  47 + int OPERATE_TYPE_2 = 2;
48 48  
49 49 /**
50 50 * 操作日志类型: 更新
51 51 */
52   - public static final int OPERATE_TYPE_3 = 3;
  52 + int OPERATE_TYPE_3 = 3;
53 53  
54 54 /**
55 55 * 操作日志类型: 删除
56 56 */
57   - public static final int OPERATE_TYPE_4 = 4;
  57 + int OPERATE_TYPE_4 = 4;
58 58  
59 59 /**
60 60 * 操作日志类型: 倒入
61 61 */
62   - public static final int OPERATE_TYPE_5 = 5;
  62 + int OPERATE_TYPE_5 = 5;
63 63  
64 64 /**
65 65 * 操作日志类型: 导出
66 66 */
67   - public static final int OPERATE_TYPE_6 = 6;
  67 + int OPERATE_TYPE_6 = 6;
68 68  
69 69  
70 70 /** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
71   - public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
  71 + Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
72 72 /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */
73   - public static final Integer SC_OK_200 = 200;
  73 + Integer SC_OK_200 = 200;
74 74  
75 75 /**访问权限认证未通过 510*/
76   - public static final Integer SC_JEECG_NO_AUTHZ=510;
  76 + Integer SC_JEECG_NO_AUTHZ=510;
77 77  
78 78 /** 登录用户Shiro权限缓存KEY前缀 */
79 79 public static String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.config.shiro.ShiroRealm.authorizationCache:";
80 80 /** 登录用户Token令牌缓存KEY前缀 */
81   - public static final String PREFIX_USER_TOKEN = "prefix_user_token_";
  81 + String PREFIX_USER_TOKEN = "prefix_user_token_";
82 82 // /** Token缓存时间:3600秒即一小时 */
83   -// public static final int TOKEN_EXPIRE_TIME = 3600;
  83 +// int TOKEN_EXPIRE_TIME = 3600;
84 84  
85 85 /** 登录二维码 */
86   - public static final String LOGIN_QRCODE_PRE = "QRCODELOGIN:";
87   - public static final String LOGIN_QRCODE = "LQ:";
  86 + String LOGIN_QRCODE_PRE = "QRCODELOGIN:";
  87 + String LOGIN_QRCODE = "LQ:";
88 88 /** 登录二维码token */
89   - public static final String LOGIN_QRCODE_TOKEN = "LQT:";
  89 + String LOGIN_QRCODE_TOKEN = "LQT:";
90 90  
91 91  
92 92 /**
93 93 * 0:一级菜单
94 94 */
95   - public static final Integer MENU_TYPE_0 = 0;
  95 + Integer MENU_TYPE_0 = 0;
96 96 /**
97 97 * 1:子菜单
98 98 */
99   - public static final Integer MENU_TYPE_1 = 1;
  99 + Integer MENU_TYPE_1 = 1;
100 100 /**
101 101 * 2:按钮权限
102 102 */
103   - public static final Integer MENU_TYPE_2 = 2;
  103 + Integer MENU_TYPE_2 = 2;
104 104  
105 105 /**通告对象类型(USER:指定用户,ALL:全体用户)*/
106   - public static final String MSG_TYPE_UESR = "USER";
107   - public static final String MSG_TYPE_ALL = "ALL";
  106 + String MSG_TYPE_UESR = "USER";
  107 + String MSG_TYPE_ALL = "ALL";
108 108  
109 109 /**发布状态(0未发布,1已发布,2已撤销)*/
110   - public static final String NO_SEND = "0";
111   - public static final String HAS_SEND = "1";
112   - public static final String HAS_CANCLE = "2";
  110 + String NO_SEND = "0";
  111 + String HAS_SEND = "1";
  112 + String HAS_CANCLE = "2";
113 113  
114 114 /**阅读状态(0未读,1已读)*/
115   - public static final String HAS_READ_FLAG = "1";
116   - public static final String NO_READ_FLAG = "0";
  115 + String HAS_READ_FLAG = "1";
  116 + String NO_READ_FLAG = "0";
117 117  
118 118 /**优先级(L低,M中,H高)*/
119   - public static final String PRIORITY_L = "L";
120   - public static final String PRIORITY_M = "M";
121   - public static final String PRIORITY_H = "H";
  119 + String PRIORITY_L = "L";
  120 + String PRIORITY_M = "M";
  121 + String PRIORITY_H = "H";
122 122  
123 123 /**
124 124 * 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板
125 125 */
126   - public static final String SMS_TPL_TYPE_0 = "0";
127   - public static final String SMS_TPL_TYPE_1 = "1";
128   - public static final String SMS_TPL_TYPE_2 = "2";
  126 + String SMS_TPL_TYPE_0 = "0";
  127 + String SMS_TPL_TYPE_1 = "1";
  128 + String SMS_TPL_TYPE_2 = "2";
129 129  
130 130 /**
131 131 * 状态(0无效1有效)
132 132 */
133   - public static final String STATUS_0 = "0";
134   - public static final String STATUS_1 = "1";
  133 + String STATUS_0 = "0";
  134 + String STATUS_1 = "1";
135 135  
136 136 /**
137 137 * 同步工作流引擎1同步0不同步
138 138 */
139   - public static final Integer ACT_SYNC_1 = 1;
140   - public static final Integer ACT_SYNC_0 = 0;
  139 + Integer ACT_SYNC_1 = 1;
  140 + Integer ACT_SYNC_0 = 0;
141 141  
142 142 /**
143 143 * 消息类型1:通知公告2:系统消息
144 144 */
145   - public static final String MSG_CATEGORY_1 = "1";
146   - public static final String MSG_CATEGORY_2 = "2";
  145 + String MSG_CATEGORY_1 = "1";
  146 + String MSG_CATEGORY_2 = "2";
147 147  
148 148 /**
149 149 * 是否配置菜单的数据权限 1是0否
150 150 */
151   - public static final Integer RULE_FLAG_0 = 0;
152   - public static final Integer RULE_FLAG_1 = 1;
  151 + Integer RULE_FLAG_0 = 0;
  152 + Integer RULE_FLAG_1 = 1;
153 153  
154 154 /**
155 155 * 是否用户已被冻结 1正常(解冻) 2冻结
156 156 */
157   - public static final Integer USER_UNFREEZE = 1;
158   - public static final Integer USER_FREEZE = 2;
  157 + Integer USER_UNFREEZE = 1;
  158 + Integer USER_FREEZE = 2;
159 159  
160 160 /**字典翻译文本后缀*/
161   - public static final String DICT_TEXT_SUFFIX = "_dictText";
  161 + String DICT_TEXT_SUFFIX = "_dictText";
162 162  
163 163 /**
164 164 * 表单设计器主表类型
165 165 */
166   - public static final Integer DESIGN_FORM_TYPE_MAIN = 1;
  166 + Integer DESIGN_FORM_TYPE_MAIN = 1;
167 167  
168 168 /**
169 169 * 表单设计器子表表类型
170 170 */
171   - public static final Integer DESIGN_FORM_TYPE_SUB = 2;
  171 + Integer DESIGN_FORM_TYPE_SUB = 2;
172 172  
173 173 /**
174 174 * 表单设计器URL授权通过
175 175 */
176   - public static final Integer DESIGN_FORM_URL_STATUS_PASSED = 1;
  176 + Integer DESIGN_FORM_URL_STATUS_PASSED = 1;
177 177  
178 178 /**
179 179 * 表单设计器URL授权未通过
180 180 */
181   - public static final Integer DESIGN_FORM_URL_STATUS_NOT_PASSED = 2;
  181 + Integer DESIGN_FORM_URL_STATUS_NOT_PASSED = 2;
182 182  
183 183 /**
184 184 * 表单设计器新增 Flag
185 185 */
186   - public static final String DESIGN_FORM_URL_TYPE_ADD = "add";
  186 + String DESIGN_FORM_URL_TYPE_ADD = "add";
187 187 /**
188 188 * 表单设计器修改 Flag
189 189 */
190   - public static final String DESIGN_FORM_URL_TYPE_EDIT = "edit";
  190 + String DESIGN_FORM_URL_TYPE_EDIT = "edit";
191 191 /**
192 192 * 表单设计器详情 Flag
193 193 */
194   - public static final String DESIGN_FORM_URL_TYPE_DETAIL = "detail";
  194 + String DESIGN_FORM_URL_TYPE_DETAIL = "detail";
195 195 /**
196 196 * 表单设计器复用数据 Flag
197 197 */
198   - public static final String DESIGN_FORM_URL_TYPE_REUSE = "reuse";
  198 + String DESIGN_FORM_URL_TYPE_REUSE = "reuse";
199 199 /**
200 200 * 表单设计器编辑 Flag (已弃用)
201 201 */
202   - public static final String DESIGN_FORM_URL_TYPE_VIEW = "view";
  202 + String DESIGN_FORM_URL_TYPE_VIEW = "view";
203 203  
204 204 /**
205 205 * online参数值设置(是:Y, 否:N)
206 206 */
207   - public static final String ONLINE_PARAM_VAL_IS_TURE = "Y";
208   - public static final String ONLINE_PARAM_VAL_IS_FALSE = "N";
  207 + String ONLINE_PARAM_VAL_IS_TURE = "Y";
  208 + String ONLINE_PARAM_VAL_IS_FALSE = "N";
209 209  
210 210 /**
211 211 * 文件上传类型(本地:local,Minio:minio,阿里云:alioss)
212 212 */
213   - public static final String UPLOAD_TYPE_LOCAL = "local";
214   - public static final String UPLOAD_TYPE_MINIO = "minio";
215   - public static final String UPLOAD_TYPE_OSS = "alioss";
  213 + String UPLOAD_TYPE_LOCAL = "local";
  214 + String UPLOAD_TYPE_MINIO = "minio";
  215 + String UPLOAD_TYPE_OSS = "alioss";
216 216  
217 217 /**
218 218 * 文档上传自定义桶名称
219 219 */
220   - public static final String UPLOAD_CUSTOM_BUCKET = "eoafile";
  220 + String UPLOAD_CUSTOM_BUCKET = "eoafile";
221 221 /**
222 222 * 文档上传自定义路径
223 223 */
224   - public static final String UPLOAD_CUSTOM_PATH = "eoafile";
  224 + String UPLOAD_CUSTOM_PATH = "eoafile";
225 225 /**
226 226 * 文件外链接有效天数
227 227 */
228   - public static final Integer UPLOAD_EFFECTIVE_DAYS = 1;
  228 + Integer UPLOAD_EFFECTIVE_DAYS = 1;
229 229  
230 230 /**
231 231 * 员工身份 (1:普通员工 2:上级)
232 232 */
233   - public static final Integer USER_IDENTITY_1 = 1;
234   - public static final Integer USER_IDENTITY_2 = 2;
  233 + Integer USER_IDENTITY_1 = 1;
  234 + Integer USER_IDENTITY_2 = 2;
235 235  
236 236 /** sys_user 表 username 唯一键索引 */
237   - public static final String SQL_INDEX_UNIQ_SYS_USER_USERNAME = "uniq_sys_user_username";
  237 + String SQL_INDEX_UNIQ_SYS_USER_USERNAME = "uniq_sys_user_username";
238 238 /** sys_user 表 work_no 唯一键索引 */
239   - public static final String SQL_INDEX_UNIQ_SYS_USER_WORK_NO = "uniq_sys_user_work_no";
  239 + String SQL_INDEX_UNIQ_SYS_USER_WORK_NO = "uniq_sys_user_work_no";
240 240 /** sys_user 表 phone 唯一键索引 */
241   - public static final String SQL_INDEX_UNIQ_SYS_USER_PHONE = "uniq_sys_user_phone";
  241 + String SQL_INDEX_UNIQ_SYS_USER_PHONE = "uniq_sys_user_phone";
242 242 /** 达梦数据库升提示。违反表[SYS_USER]唯一性约束 */
243   - public static final String SQL_INDEX_UNIQ_SYS_USER = "唯一性约束";
  243 + String SQL_INDEX_UNIQ_SYS_USER = "唯一性约束";
244 244  
245 245 /** sys_user 表 email 唯一键索引 */
246   - public static final String SQL_INDEX_UNIQ_SYS_USER_EMAIL = "uniq_sys_user_email";
  246 + String SQL_INDEX_UNIQ_SYS_USER_EMAIL = "uniq_sys_user_email";
247 247 /** sys_quartz_job 表 job_class_name 唯一键索引 */
248   - public static final String SQL_INDEX_UNIQ_JOB_CLASS_NAME = "uniq_job_class_name";
  248 + String SQL_INDEX_UNIQ_JOB_CLASS_NAME = "uniq_job_class_name";
249 249 /** sys_position 表 code 唯一键索引 */
250   - public static final String SQL_INDEX_UNIQ_CODE = "uniq_code";
  250 + String SQL_INDEX_UNIQ_CODE = "uniq_code";
251 251 /** sys_role 表 code 唯一键索引 */
252   - public static final String SQL_INDEX_UNIQ_SYS_ROLE_CODE = "uniq_sys_role_role_code";
  252 + String SQL_INDEX_UNIQ_SYS_ROLE_CODE = "uniq_sys_role_role_code";
253 253 /** sys_depart 表 code 唯一键索引 */
254   - public static final String SQL_INDEX_UNIQ_DEPART_ORG_CODE = "uniq_depart_org_code";
  254 + String SQL_INDEX_UNIQ_DEPART_ORG_CODE = "uniq_depart_org_code";
255 255 /** sys_category 表 code 唯一键索引 */
256   - public static final String SQL_INDEX_UNIQ_CATEGORY_CODE = "idx_sc_code";
  256 + String SQL_INDEX_UNIQ_CATEGORY_CODE = "idx_sc_code";
257 257 /**
258 258 * 在线聊天 是否为默认分组
259 259 */
260   - public static final String IM_DEFAULT_GROUP = "1";
  260 + String IM_DEFAULT_GROUP = "1";
261 261 /**
262 262 * 在线聊天 图片文件保存路径
263 263 */
264   - public static final String IM_UPLOAD_CUSTOM_PATH = "imfile";
  264 + String IM_UPLOAD_CUSTOM_PATH = "imfile";
265 265 /**
266 266 * 在线聊天 用户状态
267 267 */
268   - public static final String IM_STATUS_ONLINE = "online";
  268 + String IM_STATUS_ONLINE = "online";
269 269  
270 270 /**
271 271 * 在线聊天 SOCKET消息类型
272 272 */
273   - public static final String IM_SOCKET_TYPE = "chatMessage";
  273 + String IM_SOCKET_TYPE = "chatMessage";
274 274  
275 275 /**
276 276 * 在线聊天 是否开启默认添加好友 1是 0否
277 277 */
278   - public static final String IM_DEFAULT_ADD_FRIEND = "1";
  278 + String IM_DEFAULT_ADD_FRIEND = "1";
279 279  
280 280 /**
281 281 * 在线聊天 用户好友缓存前缀
282 282 */
283   - public static final String IM_PREFIX_USER_FRIEND_CACHE = "sys:cache:im:im_prefix_user_friend_";
  283 + String IM_PREFIX_USER_FRIEND_CACHE = "sys:cache:im:im_prefix_user_friend_";
284 284  
285 285 /**
286 286 * 考勤补卡业务状态 (1:同意 2:不同意)
287 287 */
288   - public static final String SIGN_PATCH_BIZ_STATUS_1 = "1";
289   - public static final String SIGN_PATCH_BIZ_STATUS_2 = "2";
  288 + String SIGN_PATCH_BIZ_STATUS_1 = "1";
  289 + String SIGN_PATCH_BIZ_STATUS_2 = "2";
290 290  
291 291 /**
292 292 * 公文文档上传自定义路径
293 293 */
294   - public static final String UPLOAD_CUSTOM_PATH_OFFICIAL = "officialdoc";
  294 + String UPLOAD_CUSTOM_PATH_OFFICIAL = "officialdoc";
295 295 /**
296 296 * 公文文档下载自定义路径
297 297 */
298   - public static final String DOWNLOAD_CUSTOM_PATH_OFFICIAL = "officaldown";
  298 + String DOWNLOAD_CUSTOM_PATH_OFFICIAL = "officaldown";
299 299  
300 300 /**
301 301 * WPS存储值类别(1 code文号 2 text(WPS模板还是公文发文模板))
302 302 */
303   - public static final String WPS_TYPE_1="1";
304   - public static final String WPS_TYPE_2="2";
  303 + String WPS_TYPE_1="1";
  304 + String WPS_TYPE_2="2";
305 305  
306 306  
307   - public final static String X_ACCESS_TOKEN = "X-Access-Token";
308   - public final static String X_SIGN = "X-Sign";
309   - public final static String X_TIMESTAMP = "X-TIMESTAMP";
310   - public final static String TOKEN_IS_INVALID_MSG = "Token失效,请重新登录!";
  307 + String X_ACCESS_TOKEN = "X-Access-Token";
  308 + String X_SIGN = "X-Sign";
  309 + String X_TIMESTAMP = "X-TIMESTAMP";
  310 + String TOKEN_IS_INVALID_MSG = "Token失效,请重新登录!";
  311 + String X_FORWARDED_SCHEME = "X-Forwarded-Scheme";
311 312  
312 313 /**
313 314 * 多租户 请求头
314 315 */
315   - public final static String TENANT_ID = "tenant-id";
  316 + String TENANT_ID = "tenant-id";
316 317  
317 318 /**
318 319 * 微服务读取配置文件属性 服务地址
319 320 */
320   - public final static String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr";
  321 + String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr";
321 322  
322 323 /**
323 324 * 第三方登录 验证密码/创建用户 都需要设置一个操作码 防止被恶意调用
324 325 */
325   - public final static String THIRD_LOGIN_CODE = "third_login_code";
  326 + String THIRD_LOGIN_CODE = "third_login_code";
326 327  
327 328 /**
328 329 * 第三方APP同步方向:本地 --> 第三方APP
... ... @@ -361,16 +362,43 @@ public interface CommonConstant {
361 362 /**String 类型的空值*/
362 363 String STRING_NULL = "null";
363 364  
364   - /**java.util.Date 包*/
365   - String JAVA_UTIL_DATE = "java.util.Date";
  365 + /**前端vue3版本Header参数名*/
  366 + String VERSION="X-Version";
366 367  
367   - /**.do*/
368   - String SPOT_DO = ".do";
  368 + /**存储在线程变量里的动态表名*/
  369 + String DYNAMIC_TABLE_NAME="DYNAMIC_TABLE_NAME";
  370 + /**
  371 + * http:// http协议
  372 + */
  373 + String HTTP_PROTOCOL = "http://";
369 374  
  375 + /**
  376 + * https:// https协议
  377 + */
  378 + String HTTPS_PROTOCOL = "https://";
  379 +
  380 + /** 部门表唯一key,id */
  381 + String DEPART_KEY_ID = "id";
  382 + /** 部门表唯一key,orgCode */
  383 + String DEPART_KEY_ORG_CODE = "orgCode";
370 384  
371   - /**前端vue版本标识*/
372   - String VERSION="X-Version";
  385 + /**
  386 + * 发消息 会传递一些信息到map
  387 + */
  388 + String NOTICE_MSG_SUMMARY = "NOTICE_MSG_SUMMARY";
373 389  
374   - /**前端vue版本*/
375   - String VERSION_VUE3="vue3";
  390 + /**
  391 + * 发消息 会传递一个业务ID到map
  392 + */
  393 + String NOTICE_MSG_BUS_ID = "NOTICE_MSG_BUS_ID";
  394 +
  395 + /**
  396 + * 邮箱消息中地址登录时地址后携带的token,需要替换成真实的token值
  397 + */
  398 + String LOGIN_TOKEN = "{LOGIN_TOKEN}";
  399 +
  400 + /**
  401 + * 模板消息中 跳转地址的对应的key
  402 + */
  403 + String MSG_HREF_URL = "url";
376 404 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DataBaseConstant.java
... ... @@ -153,4 +153,14 @@ public interface DataBaseConstant {
153 153 * sql语句 where
154 154 */
155 155 String SQL_WHERE = "where";
  156 +
  157 + /**
  158 + * sql语句 asc
  159 + */
  160 + String SQL_ASC = "asc";
  161 +
  162 + /**
  163 + * sqlserver数据库,中间有空格
  164 + */
  165 + String DB_TYPE_SQL_SERVER_BLANK = "sql server";
156 166 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DynamicTableConstant.java 0 → 100644
  1 +package org.jeecg.common.constant;
  2 +
  3 +/**
  4 + * 动态切换表配置常量
  5 + *
  6 + * @author: scott
  7 + * @date: 2022年04月25日 22:30
  8 + */
  9 +public class DynamicTableConstant {
  10 + /**
  11 + * 角色首页配置表
  12 + * vue2表名: sys_role_index
  13 + * vue3表名: sys_role_index_vue3
  14 + */
  15 + public static final String SYS_ROLE_INDEX = "sys_role_index";
  16 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ProvinceCityArea.java
... ... @@ -35,9 +35,13 @@ public class ProvinceCityArea {
35 35 this.initAreaList();
36 36 if(areaList!=null && areaList.size()>0){
37 37 for(int i=areaList.size()-1;i>=0;i--){
38   - if(text.indexOf(areaList.get(i).getText())>=0){
  38 + //update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
  39 + String areaText = areaList.get(i).getText();
  40 + String cityText = areaList.get(i).getAheadText();
  41 + if(text.indexOf(areaText)>=0 && (cityText!=null && text.indexOf(cityText)>=0)){
39 42 return areaList.get(i).getId();
40 43 }
  44 + //update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
41 45 }
42 46 }
43 47 return null;
... ... @@ -145,6 +149,9 @@ public class ProvinceCityArea {
145 149 for(String areaKey:areaJson.keySet()){
146 150 //System.out.println("········"+areaKey);
147 151 Area area = new Area(areaKey,areaJson.getString(areaKey),cityKey);
  152 + //update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
  153 + area.setAheadText(cityJson.getString(cityKey));
  154 + //update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
148 155 this.areaList.add(area);
149 156 }
150 157 }
... ... @@ -180,6 +187,8 @@ public class ProvinceCityArea {
180 187 String id;
181 188 String text;
182 189 String pid;
  190 + // 用于存储上级文本数据,区的上级文本 是市的数据
  191 + String aheadText;
183 192  
184 193 public Area(String id,String text,String pid){
185 194 this.id = id;
... ... @@ -198,5 +207,12 @@ public class ProvinceCityArea {
198 207 public String getPid() {
199 208 return pid;
200 209 }
  210 +
  211 + public String getAheadText() {
  212 + return aheadText;
  213 + }
  214 + public void setAheadText(String aheadText) {
  215 + this.aheadText = aheadText;
  216 + }
201 217 }
202 218 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java
... ... @@ -86,4 +86,34 @@ public class SymbolConstant {
86 86 * 符号:和 &
87 87 */
88 88 public static final String AND = "&";
  89 +
  90 + /**
  91 + * 符号:../
  92 + */
  93 + public static final String SPOT_SINGLE_SLASH = "../";
  94 +
  95 + /**
  96 + * 符号:..\\
  97 + */
  98 + public static final String SPOT_DOUBLE_BACKSLASH = "..\\";
  99 +
  100 + /**
  101 + * 系统变量前缀 #{
  102 + */
  103 + public static final String SYS_VAR_PREFIX = "#{";
  104 +
  105 + /**
  106 + * 符号 {{
  107 + */
  108 + public static final String DOUBLE_LEFT_CURLY_BRACKET = "{{";
  109 +
  110 + /**
  111 + * 符号:[
  112 + */
  113 + public static final String SQUARE_BRACKETS_LEFT = "[";
  114 + /**
  115 + * 符号:]
  116 + */
  117 + public static final String SQUARE_BRACKETS_RIGHT = "]";
  118 +
89 119 }
90 120 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/CgformEnum.java
... ... @@ -20,13 +20,14 @@ public enum CgformEnum {
20 20 */
21 21 MANY(2, "many", "/jeecg/code-template-online", "default.onetomany", "经典风格"),
22 22 /**
23   - * 多表
24   - */
25   - ERP(2, "erp", "/jeecg/code-template-online", "erp.onetomany", "ERP风格"),
26   - /**
27 23 * 多表(jvxe风格)
28 24 * */
29 25 JVXE_TABLE(2, "jvxe", "/jeecg/code-template-online", "jvxe.onetomany", "JVXE风格"),
  26 +
  27 + /**
  28 + * 多表
  29 + */
  30 + ERP(2, "erp", "/jeecg/code-template-online", "erp.onetomany", "ERP风格"),
30 31 /**
31 32 * 多表(内嵌子表风格)
32 33 */
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/LowAppAopEnum.java
... ... @@ -15,6 +15,8 @@ public enum LowAppAopEnum {
15 15 * 删除方法(包含单个和批量删除)
16 16 */
17 17 DELETE,
  18 + /** 复制表单操作 */
  19 + COPY,
18 20  
19 21 /**
20 22 * Online表单专用:数据库表转Online表单
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/MessageTypeEnum.java 0 → 100644
  1 +package org.jeecg.common.constant.enums;
  2 +
  3 +import org.jeecg.common.system.annotation.EnumDict;
  4 +import org.jeecg.common.system.vo.DictModel;
  5 +
  6 +import java.util.ArrayList;
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * 消息类型
  11 + * @author: jeecg-boot
  12 + */
  13 +@EnumDict("messageType")
  14 +public enum MessageTypeEnum {
  15 +
  16 + XT("system", "系统消息"),
  17 + YJ("email", "邮件消息"),
  18 + DD("dingtalk", "钉钉消息"),
  19 + QYWX("wechat_enterprise", "企业微信");
  20 +
  21 + MessageTypeEnum(String type, String note){
  22 + this.type = type;
  23 + this.note = note;
  24 + }
  25 +
  26 + /**
  27 + * 消息类型
  28 + */
  29 + String type;
  30 +
  31 + /**
  32 + * 类型说明
  33 + */
  34 + String note;
  35 +
  36 + public String getNote() {
  37 + return note;
  38 + }
  39 +
  40 + public void setNote(String note) {
  41 + this.note = note;
  42 + }
  43 +
  44 + public String getType() {
  45 + return type;
  46 + }
  47 +
  48 + public void setType(String type) {
  49 + this.type = type;
  50 + }
  51 +
  52 +
  53 + /**
  54 + * 获取字典数据
  55 + * @return
  56 + */
  57 + public static List<DictModel> getDictList(){
  58 + List<DictModel> list = new ArrayList<>();
  59 + DictModel dictModel = null;
  60 + for(MessageTypeEnum e: MessageTypeEnum.values()){
  61 + dictModel = new DictModel();
  62 + dictModel.setValue(e.getType());
  63 + dictModel.setText(e.getNote());
  64 + list.add(dictModel);
  65 + }
  66 + return list;
  67 + }
  68 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java
... ... @@ -123,7 +123,8 @@ public class JeecgBootExceptionHandler {
123 123 @ExceptionHandler(DataIntegrityViolationException.class)
124 124 public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
125 125 log.error(e.getMessage(), e);
126   - return Result.error("字段太长,超出数据库字段的长度");
  126 + //【issues/3624】数据库执行异常handleDataIntegrityViolationException提示有误 #3624
  127 + return Result.error("执行数据库异常,违反了完整性例如:违反惟一约束、违反非空限制、字段内容超出长度等");
127 128 }
128 129  
129 130 @ExceptionHandler(PoolException.class)
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/annotation/EnumDict.java 0 → 100644
  1 +package org.jeecg.common.system.annotation;
  2 +
  3 +import java.lang.annotation.*;
  4 +
  5 +/**
  6 + * 将枚举类转化成字典数据
  7 + * @Author taoYan
  8 + * @Date 2022/7/8 10:34
  9 + **/
  10 +@Target(ElementType.TYPE)
  11 +@Retention(RetentionPolicy.RUNTIME)
  12 +@Documented
  13 +public @interface EnumDict {
  14 +
  15 + /**
  16 + * 作为字典数据的唯一编码
  17 + */
  18 + String value() default "";
  19 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java
... ... @@ -53,18 +53,14 @@ public class JeecgController&lt;T, S extends IService&lt;T&gt;&gt; {
53 53 QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
54 54 LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
55 55  
56   - // Step.2 获取导出数据
57   - List<T> pageList = service.list(queryWrapper);
58   - List<T> exportList = null;
59   -
60 56 // 过滤选中数据
61 57 String selections = request.getParameter("selections");
62 58 if (oConvertUtils.isNotEmpty(selections)) {
63 59 List<String> selectionList = Arrays.asList(selections.split(","));
64   - exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
65   - } else {
66   - exportList = pageList;
  60 + queryWrapper.in("id",selectionList);
67 61 }
  62 + // Step.2 获取导出数据
  63 + List<T> exportList = service.list(queryWrapper);
68 64  
69 65 // Step.3 AutoPoi 导出Excel
70 66 ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
... ... @@ -97,21 +93,20 @@ public class JeecgController&lt;T, S extends IService&lt;T&gt;&gt; {
97 93 // Step.2 计算分页sheet数据
98 94 double total = service.count();
99 95 int count = (int)Math.ceil(total/pageNum);
100   - // Step.3 多sheet处理
  96 + //update-begin-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 ---
  97 + // Step.3 过滤选中数据
  98 + String selections = request.getParameter("selections");
  99 + if (oConvertUtils.isNotEmpty(selections)) {
  100 + List<String> selectionList = Arrays.asList(selections.split(","));
  101 + queryWrapper.in("id",selectionList);
  102 + }
  103 + //update-end-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 ---
  104 + // Step.4 多sheet处理
101 105 List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
102 106 for (int i = 1; i <=count ; i++) {
103 107 Page<T> page = new Page<T>(i, pageNum);
104 108 IPage<T> pageList = service.page(page, queryWrapper);
105   - List<T> records = pageList.getRecords();
106   - List<T> exportList = null;
107   - // 过滤选中数据
108   - String selections = request.getParameter("selections");
109   - if (oConvertUtils.isNotEmpty(selections)) {
110   - List<String> selectionList = Arrays.asList(selections.split(","));
111   - exportList = records.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
112   - } else {
113   - exportList = records;
114   - }
  109 + List<T> exportList = pageList.getRecords();
115 110 Map<String, Object> map = new HashMap<>(5);
116 111 ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title+i,upLoadPath);
117 112 exportParams.setType(ExcelType.XSSF);
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java
... ... @@ -16,6 +16,7 @@ import org.apache.commons.beanutils.PropertyUtils;
16 16 import org.jeecg.common.constant.CommonConstant;
17 17 import org.jeecg.common.constant.DataBaseConstant;
18 18 import org.jeecg.common.constant.SymbolConstant;
  19 +import org.jeecg.common.exception.JeecgBootException;
19 20 import org.jeecg.common.system.util.JeecgDataAutorUtils;
20 21 import org.jeecg.common.system.util.JwtUtil;
21 22 import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
... ... @@ -191,8 +192,8 @@ public class QueryGenerator {
191 192 log.error(e.getMessage(), e);
192 193 }
193 194 }
194   - // 排序逻辑 处理
195   - doMultiFieldsOrder(queryWrapper, parameterMap);
  195 + // 排序逻辑 处理
  196 + doMultiFieldsOrder(queryWrapper, parameterMap, fieldColumnMap.keySet());
196 197  
197 198 //高级查询
198 199 doSuperQuery(queryWrapper, parameterMap, fieldColumnMap);
... ... @@ -228,8 +229,7 @@ public class QueryGenerator {
228 229 }
229 230 }
230 231  
231   - /**多字段排序 TODO 需要修改前端*/
232   - private static void doMultiFieldsOrder(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap) {
  232 + private static void doMultiFieldsOrder(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap, Set<String> allFields) {
233 233 String column=null,order=null;
234 234 if(parameterMap!=null&& parameterMap.containsKey(ORDER_COLUMN)) {
235 235 column = parameterMap.get(ORDER_COLUMN)[0];
... ... @@ -243,6 +243,15 @@ public class QueryGenerator {
243 243 if(column.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
244 244 column = column.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX));
245 245 }
  246 +
  247 + //update-begin-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时,使用SQL注入生效
  248 + //判断column是不是当前实体的
  249 + log.info("当前字段有:"+ allFields);
  250 + if (!allColumnExist(column, allFields)) {
  251 + throw new JeecgBootException("请注意,将要排序的列字段不存在:" + column);
  252 + }
  253 + //update-end-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时,使用SQL注入生效
  254 +
246 255 //SQL注入check
247 256 SqlInjectionUtil.filterContent(column);
248 257  
... ... @@ -264,6 +273,28 @@ public class QueryGenerator {
264 273 //update-end--Author:scott Date:20210531 for:36 多条件排序无效问题修正-------
265 274 }
266 275 }
  276 +
  277 + //update-begin-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时,使用SQL注入生效
  278 + /**
  279 + * 多字段排序 判断所传字段是否存在
  280 + * @return
  281 + */
  282 + private static boolean allColumnExist(String columnStr, Set<String> allFields){
  283 + boolean exist = true;
  284 + if(columnStr.indexOf(COMMA)>=0){
  285 + String[] arr = columnStr.split(COMMA);
  286 + for(String column: arr){
  287 + if(!allFields.contains(column)){
  288 + exist = false;
  289 + break;
  290 + }
  291 + }
  292 + }else{
  293 + exist = allFields.contains(columnStr);
  294 + }
  295 + return exist;
  296 + }
  297 + //update-end-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时,使用SQL注入生效
267 298  
268 299 /**
269 300 * 高级查询
... ... @@ -825,13 +856,13 @@ public class QueryGenerator {
825 856 res = field + " in "+getInConditionValue(value, isString);
826 857 break;
827 858 case LIKE:
828   - res = field + " like "+getLikeConditionValue(value);
  859 + res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LIKE);
829 860 break;
830 861 case LEFT_LIKE:
831   - res = field + " like "+getLikeConditionValue(value);
  862 + res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LEFT_LIKE);
832 863 break;
833 864 case RIGHT_LIKE:
834   - res = field + " like "+getLikeConditionValue(value);
  865 + res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.RIGHT_LIKE);
835 866 break;
836 867 default:
837 868 res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
... ... @@ -914,8 +945,15 @@ public class QueryGenerator {
914 945 }
915 946 //update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
916 947 }
917   -
918   - private static String getLikeConditionValue(Object value) {
  948 +
  949 + /**
  950 + * 先根据值判断 走左模糊还是右模糊
  951 + * 最后如果值不带任何标识(*或者%),则再根据ruleEnum判断
  952 + * @param value
  953 + * @param ruleEnum
  954 + * @return
  955 + */
  956 + private static String getLikeConditionValue(Object value, QueryRuleEnum ruleEnum) {
919 957 String str = value.toString().trim();
920 958 if(str.startsWith(SymbolConstant.ASTERISK) && str.endsWith(SymbolConstant.ASTERISK)) {
921 959 if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
... ... @@ -951,11 +989,30 @@ public class QueryGenerator {
951 989 }
952 990 }
953 991 }else {
954   - if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
955   - return "N'%"+str+"%'";
956   - }else{
957   - return "'%"+str+"%'";
  992 +
  993 + //update-begin-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
  994 + // 走到这里说明 value不带有任何模糊查询的标识(*或者%)
  995 + if (ruleEnum == QueryRuleEnum.LEFT_LIKE) {
  996 + if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
  997 + return "N'%" + str + "'";
  998 + } else {
  999 + return "'%" + str + "'";
  1000 + }
  1001 + } else if (ruleEnum == QueryRuleEnum.RIGHT_LIKE) {
  1002 + if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
  1003 + return "N'" + str + "%'";
  1004 + } else {
  1005 + return "'" + str + "%'";
  1006 + }
  1007 + } else {
  1008 + if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
  1009 + return "N'%" + str + "%'";
  1010 + } else {
  1011 + return "'%" + str + "%'";
  1012 + }
958 1013 }
  1014 + //update-end-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
  1015 +
959 1016 }
960 1017 }
961 1018 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java
... ... @@ -54,7 +54,7 @@ public class JwtUtil {
54 54 try {
55 55 os = httpServletResponse.getOutputStream();
56 56 httpServletResponse.setCharacterEncoding("UTF-8");
57   - httpServletResponse.setStatus(401);
  57 + httpServletResponse.setStatus(code);
58 58 os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
59 59 os.flush();
60 60 os.close();
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/ResourceUtil.java 0 → 100644
  1 +package org.jeecg.common.system.util;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.jeecg.common.system.annotation.EnumDict;
  5 +import org.jeecg.common.system.vo.DictModel;
  6 +import org.jeecg.common.util.oConvertUtils;
  7 +import org.springframework.core.io.Resource;
  8 +import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  9 +import org.springframework.core.io.support.ResourcePatternResolver;
  10 +import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
  11 +import org.springframework.core.type.classreading.MetadataReader;
  12 +import org.springframework.core.type.classreading.MetadataReaderFactory;
  13 +import org.springframework.util.ClassUtils;
  14 +
  15 +import java.util.ArrayList;
  16 +import java.util.HashMap;
  17 +import java.util.List;
  18 +import java.util.Map;
  19 +
  20 +/**
  21 + * 资源加载工具类
  22 + * @Author taoYan
  23 + * @Date 2022/7/8 10:40
  24 + **/
  25 +@Slf4j
  26 +public class ResourceUtil {
  27 +
  28 +
  29 + /**
  30 + * 枚举字典数据
  31 + */
  32 + private final static Map<String, List<DictModel>> enumDictData = new HashMap<>(5);
  33 +
  34 + /**
  35 + * 所有java类
  36 + */
  37 + private final static String CLASS_PATTERN="/**/*.class";
  38 +
  39 + /**
  40 + * 包路径 org.jeecg
  41 + */
  42 + private final static String BASE_PACKAGE = "org.jeecg";
  43 +
  44 + /**
  45 + * 枚举类中获取字典数据的方法名
  46 + */
  47 + private final static String METHOD_NAME = "getDictList";
  48 +
  49 + /**
  50 + * 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems()
  51 + * @return
  52 + */
  53 + public static Map<String, List<DictModel>> getEnumDictData(){
  54 + if(enumDictData.keySet().size()>0){
  55 + return enumDictData;
  56 + }
  57 + ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
  58 + String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_PATTERN;
  59 + try {
  60 + Resource[] resources = resourcePatternResolver.getResources(pattern);
  61 + MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
  62 + for (Resource resource : resources) {
  63 + MetadataReader reader = readerFactory.getMetadataReader(resource);
  64 + String classname = reader.getClassMetadata().getClassName();
  65 + Class<?> clazz = Class.forName(classname);
  66 + EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
  67 + if (enumDict != null) {
  68 + EnumDict annotation = clazz.getAnnotation(EnumDict.class);
  69 + String key = annotation.value();
  70 + if(oConvertUtils.isNotEmpty(key)){
  71 + List<DictModel> list = (List<DictModel>) clazz.getDeclaredMethod(METHOD_NAME).invoke(null);
  72 + enumDictData.put(key, list);
  73 + }
  74 + }
  75 + }
  76 + }catch (Exception e){
  77 + log.error("获取枚举类字典数据异常", e.getMessage());
  78 + // e.printStackTrace();
  79 + }
  80 + return enumDictData;
  81 + }
  82 +
  83 + /**
  84 + * 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
  85 + * @param dictCodeList
  86 + * @param keys
  87 + * @return
  88 + */
  89 + public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys){
  90 + if(enumDictData.keySet().size()==0){
  91 + getEnumDictData();
  92 + }
  93 + Map<String, List<DictModel>> map = new HashMap<>();
  94 + for (String code : enumDictData.keySet()) {
  95 + if(dictCodeList.indexOf(code)>=0){
  96 + List<DictModel> dictItemList = enumDictData.get(code);
  97 + for(DictModel dm: dictItemList){
  98 + String value = dm.getValue();
  99 + if(keys.indexOf(value)>=0){
  100 + List<DictModel> list = new ArrayList<>();
  101 + list.add(new DictModel(value, dm.getText()));
  102 + map.put(code,list);
  103 + break;
  104 + }
  105 + }
  106 + }
  107 + }
  108 + return map;
  109 + }
  110 +
  111 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java
... ... @@ -2,6 +2,7 @@ package org.jeecg.common.system.vo;
2 2  
3 3 import java.util.Date;
4 4  
  5 +import org.jeecg.common.desensitization.annotation.SensitiveField;
5 6 import org.springframework.format.annotation.DateTimeFormat;
6 7  
7 8 import com.fasterxml.jackson.annotation.JsonFormat;
... ... @@ -26,21 +27,25 @@ public class LoginUser {
26 27 /**
27 28 * 登录人id
28 29 */
  30 + @SensitiveField
29 31 private String id;
30 32  
31 33 /**
32 34 * 登录人账号
33 35 */
  36 + @SensitiveField
34 37 private String username;
35 38  
36 39 /**
37 40 * 登录人名字
38 41 */
  42 + @SensitiveField
39 43 private String realname;
40 44  
41 45 /**
42 46 * 登录人密码
43 47 */
  48 + @SensitiveField
44 49 private String password;
45 50  
46 51 /**
... ... @@ -50,11 +55,13 @@ public class LoginUser {
50 55 /**
51 56 * 头像
52 57 */
  58 + @SensitiveField
53 59 private String avatar;
54 60  
55 61 /**
56 62 * 生日
57 63 */
  64 + @SensitiveField
58 65 @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
59 66 @DateTimeFormat(pattern = "yyyy-MM-dd")
60 67 private Date birthday;
... ... @@ -67,11 +74,13 @@ public class LoginUser {
67 74 /**
68 75 * 电子邮件
69 76 */
  77 + @SensitiveField
70 78 private String email;
71 79  
72 80 /**
73 81 * 电话
74 82 */
  83 + @SensitiveField
75 84 private String phone;
76 85  
77 86 /**
... ... @@ -103,11 +112,13 @@ public class LoginUser {
103 112 /**
104 113 * 职务,关联职务表
105 114 */
  115 + @SensitiveField
106 116 private String post;
107 117  
108 118 /**
109 119 * 座机号
110 120 */
  121 + @SensitiveField
111 122 private String telephone;
112 123  
113 124 /**多租户id配置,编辑用户的时候设置*/
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java
... ... @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
7 7 import lombok.extern.slf4j.Slf4j;
8 8 import org.jeecg.common.constant.CommonConstant;
9 9 import org.jeecg.common.constant.DataBaseConstant;
  10 +import org.jeecg.common.constant.ServiceNameConstants;
10 11 import org.jeecg.common.constant.SymbolConstant;
11 12 import org.jeecg.common.util.filter.FileTypeFilter;
12 13 import org.jeecg.common.util.oss.OssBootUtil;
... ... @@ -314,14 +315,14 @@ public class CommonUtils {
314 315 */
315 316 public static String getBaseUrl(HttpServletRequest request) {
316 317 //1.【兼容】兼容微服务下的 base path-------
317   - String xGatewayBasePath = request.getHeader("X_GATEWAY_BASE_PATH");
  318 + String xGatewayBasePath = request.getHeader(ServiceNameConstants.X_GATEWAY_BASE_PATH);
318 319 if(oConvertUtils.isNotEmpty(xGatewayBasePath)){
319 320 log.info("x_gateway_base_path = "+ xGatewayBasePath);
320 321 return xGatewayBasePath;
321 322 }
322 323 //2.【兼容】SSL认证之后,request.getScheme()获取不到https的问题
323 324 // https://blog.csdn.net/weixin_34376986/article/details/89767950
324   - String scheme = request.getHeader("X-Forwarded-Scheme");
  325 + String scheme = request.getHeader(CommonConstant.X_FORWARDED_SCHEME);
325 326 if(oConvertUtils.isEmpty(scheme)){
326 327 scheme = request.getScheme();
327 328 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestUtil.java
... ... @@ -214,7 +214,7 @@ public class RestUtil {
214 214 }
215 215 }
216 216 // 拼接 url 参数
217   - if (variables != null) {
  217 + if (variables != null && !variables.isEmpty()) {
218 218 url += ("?" + asUrlVariables(variables));
219 219 }
220 220 // 发送请求
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java
... ... @@ -4,6 +4,8 @@ import cn.hutool.crypto.SecureUtil;
4 4 import lombok.extern.slf4j.Slf4j;
5 5 import org.jeecg.common.exception.JeecgBootException;
6 6 import javax.servlet.http.HttpServletRequest;
  7 +import java.lang.reflect.Field;
  8 +import java.util.Set;
7 9 import java.util.regex.Pattern;
8 10  
9 11 /**
... ... @@ -20,7 +22,11 @@ public class SqlInjectionUtil {
20 22 private final static String TABLE_DICT_SIGN_SALT = "20200501";
21 23 private final static String XSS_STR = "and |exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()";
22 24  
23   - /**show tables*/
  25 + /**
  26 + * 正则 user() 匹配更严谨
  27 + */
  28 + private final static String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)";
  29 + /**正则 show tables*/
24 30 private final static String SHOW_TABLES = "show\\s+tables";
25 31  
26 32 /**
... ... @@ -42,6 +48,13 @@ public class SqlInjectionUtil {
42 48 log.info(" 表字典,SQL注入漏洞签名校验成功!sign=" + sign + ",dictCode=" + dictCode);
43 49 }
44 50  
  51 + /**
  52 + * sql注入过滤处理,遇到注入关键字抛异常
  53 + * @param value
  54 + */
  55 + public static void filterContent(String value) {
  56 + filterContent(value, null);
  57 + }
45 58  
46 59 /**
47 60 * sql注入过滤处理,遇到注入关键字抛异常
... ... @@ -49,7 +62,7 @@ public class SqlInjectionUtil {
49 62 * @param value
50 63 * @return
51 64 */
52   - public static void filterContent(String value) {
  65 + public static void filterContent(String value, String customXssString) {
53 66 if (value == null || "".equals(value)) {
54 67 return;
55 68 }
... ... @@ -66,7 +79,19 @@ public class SqlInjectionUtil {
66 79 throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
67 80 }
68 81 }
69   - if(Pattern.matches(SHOW_TABLES, value)){
  82 + //update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号
  83 + if (customXssString != null) {
  84 + String[] xssArr2 = customXssString.split("\\|");
  85 + for (int i = 0; i < xssArr2.length; i++) {
  86 + if (value.indexOf(xssArr2[i]) > -1) {
  87 + log.error("请注意,存在SQL注入关键词---> {}", xssArr2[i]);
  88 + log.error("请注意,值可能存在SQL注入风险!---> {}", value);
  89 + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
  90 + }
  91 + }
  92 + }
  93 + //update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号
  94 + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
70 95 throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
71 96 }
72 97 return;
... ... @@ -74,11 +99,19 @@ public class SqlInjectionUtil {
74 99  
75 100 /**
76 101 * sql注入过滤处理,遇到注入关键字抛异常
  102 + * @param values
  103 + */
  104 + public static void filterContent(String[] values) {
  105 + filterContent(values, null);
  106 + }
  107 +
  108 + /**
  109 + * sql注入过滤处理,遇到注入关键字抛异常
77 110 *
78 111 * @param values
79 112 * @return
80 113 */
81   - public static void filterContent(String[] values) {
  114 + public static void filterContent(String[] values, String customXssString) {
82 115 String[] xssArr = XSS_STR.split("\\|");
83 116 for (String value : values) {
84 117 if (value == null || "".equals(value)) {
... ... @@ -96,7 +129,19 @@ public class SqlInjectionUtil {
96 129 throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
97 130 }
98 131 }
99   - if(Pattern.matches(SHOW_TABLES, value)){
  132 + //update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号
  133 + if (customXssString != null) {
  134 + String[] xssArr2 = customXssString.split("\\|");
  135 + for (int i = 0; i < xssArr2.length; i++) {
  136 + if (value.indexOf(xssArr2[i]) > -1) {
  137 + log.error("请注意,存在SQL注入关键词---> {}", xssArr2[i]);
  138 + log.error("请注意,值可能存在SQL注入风险!---> {}", value);
  139 + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
  140 + }
  141 + }
  142 + }
  143 + //update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号
  144 + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
100 145 throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
101 146 }
102 147 }
... ... @@ -111,8 +156,8 @@ public class SqlInjectionUtil {
111 156 * @return
112 157 */
113 158 //@Deprecated
114   - public static void specialFilterContent(String value) {
115   - String specialXssStr = " exec | insert | select | delete | update | drop | count | chr | mid | master | truncate | char | declare |;|+|";
  159 + public static void specialFilterContentForDictSql(String value) {
  160 + String specialXssStr = " exec | insert | select | delete | update | drop | count | chr | mid | master | truncate | char | declare |;|+|user()";
116 161 String[] xssArr = specialXssStr.split("\\|");
117 162 if (value == null || "".equals(value)) {
118 163 return;
... ... @@ -129,7 +174,7 @@ public class SqlInjectionUtil {
129 174 throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
130 175 }
131 176 }
132   - if(Pattern.matches(SHOW_TABLES, value)){
  177 + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
133 178 throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
134 179 }
135 180 return;
... ... @@ -144,7 +189,7 @@ public class SqlInjectionUtil {
144 189 */
145 190 //@Deprecated
146 191 public static void specialFilterContentForOnlineReport(String value) {
147   - String specialXssStr = " exec | insert | delete | update | drop | chr | mid | master | truncate | char | declare |";
  192 + String specialXssStr = " exec | insert | delete | update | drop | chr | mid | master | truncate | char | declare |user()";
148 193 String[] xssArr = specialXssStr.split("\\|");
149 194 if (value == null || "".equals(value)) {
150 195 return;
... ... @@ -162,10 +207,53 @@ public class SqlInjectionUtil {
162 207 }
163 208 }
164 209  
165   - if(Pattern.matches(SHOW_TABLES, value)){
  210 + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
166 211 throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
167 212 }
168 213 return;
169 214 }
170 215  
  216 +
  217 + /**
  218 + * 判断给定的字段是不是类中的属性
  219 + * @param field 字段名
  220 + * @param clazz 类对象
  221 + * @return
  222 + */
  223 + public static boolean isClassField(String field, Class clazz){
  224 + Field[] fields = clazz.getDeclaredFields();
  225 + for(int i=0;i<fields.length;i++){
  226 + String fieldName = fields[i].getName();
  227 + String tableColumnName = oConvertUtils.camelToUnderline(fieldName);
  228 + if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
  229 + return true;
  230 + }
  231 + }
  232 + return false;
  233 + }
  234 +
  235 + /**
  236 + * 判断给定的多个字段是不是类中的属性
  237 + * @param fieldSet 字段名set
  238 + * @param clazz 类对象
  239 + * @return
  240 + */
  241 + public static boolean isClassField(Set<String> fieldSet, Class clazz){
  242 + Field[] fields = clazz.getDeclaredFields();
  243 + for(String field: fieldSet){
  244 + boolean exist = false;
  245 + for(int i=0;i<fields.length;i++){
  246 + String fieldName = fields[i].getName();
  247 + String tableColumnName = oConvertUtils.camelToUnderline(fieldName);
  248 + if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
  249 + exist = true;
  250 + break;
  251 + }
  252 + }
  253 + if(!exist){
  254 + return false;
  255 + }
  256 + }
  257 + return true;
  258 + }
171 259 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java
... ... @@ -5,6 +5,7 @@ import org.apache.commons.lang3.StringUtils;
5 5 import org.jeecg.common.api.CommonAPI;
6 6 import org.jeecg.common.constant.CacheConstant;
7 7 import org.jeecg.common.constant.CommonConstant;
  8 +import org.jeecg.common.desensitization.util.SensitiveInfoUtil;
8 9 import org.jeecg.common.exception.JeecgBoot401Exception;
9 10 import org.jeecg.common.system.util.JwtUtil;
10 11 import org.jeecg.common.system.vo.LoginUser;
... ... @@ -106,9 +107,16 @@ public class TokenUtils {
106 107 public static LoginUser getLoginUser(String username, CommonAPI commonApi, RedisUtil redisUtil) {
107 108 LoginUser loginUser = null;
108 109 String loginUserKey = CacheConstant.SYS_USERS_CACHE + "::" + username;
109   - if(redisUtil.hasKey(loginUserKey)){
110   - loginUser = (LoginUser) redisUtil.get(loginUserKey);
111   - }else{
  110 + //【重要】此处通过redis原生获取缓存用户,是为了解决微服务下system服务挂了,其他服务互调不通问题---
  111 + if (redisUtil.hasKey(loginUserKey)) {
  112 + try {
  113 + loginUser = (LoginUser) redisUtil.get(loginUserKey);
  114 + //解密用户
  115 + SensitiveInfoUtil.handlerObject(loginUser, false);
  116 + } catch (IllegalAccessException e) {
  117 + e.printStackTrace();
  118 + }
  119 + } else {
112 120 // 查询用户信息
113 121 loginUser = commonApi.getUserByName(username);
114 122 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java
... ... @@ -66,22 +66,20 @@ public class AesEncryptUtil {
66 66 * @throws Exception
67 67 */
68 68 public static String desEncrypt(String data, String key, String iv) throws Exception {
69   - try {
70   - byte[] encrypted1 = Base64.decode(data);
  69 + //update-begin-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理
  70 + byte[] encrypted1 = Base64.decode(data);
71 71  
72   - Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
73   - SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
74   - IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
  72 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
  73 + SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
  74 + IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
75 75  
76   - cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
  76 + cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
77 77  
78   - byte[] original = cipher.doFinal(encrypted1);
79   - String originalString = new String(original);
80   - return originalString;
81   - } catch (Exception e) {
82   - e.printStackTrace();
83   - return null;
84   - }
  78 + byte[] original = cipher.doFinal(encrypted1);
  79 + String originalString = new String(original);
  80 + //加密解码后的字符串会出现\u0000
  81 + return originalString.replaceAll("\\u0000", "");
  82 + //update-end-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理
85 83 }
86 84  
87 85 /**
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/AbstractQueryBlackListHandler.java
... ... @@ -168,7 +168,7 @@ public abstract class AbstractQueryBlackListHandler {
168 168  
169 169 public String getError(){
170 170 // TODO
171   - return "sql黑名单校验不通过,请联系管理员!";
  171 + return "系统设置了安全规则,敏感表和敏感字段禁止查询,联系管理员授权!";
172 172 }
173 173  
174 174 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeeccgBaseConfig.java renamed to jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java
1 1 package org.jeecg.config;
2 2  
  3 +import org.jeecg.config.vo.DomainUrl;
  4 +import org.jeecg.config.vo.Path;
3 5 import org.jeecg.config.vo.Shiro;
4 6 import org.springframework.boot.context.properties.ConfigurationProperties;
5 7 import org.springframework.stereotype.Component;
... ... @@ -9,9 +11,15 @@ import org.springframework.stereotype.Component;
9 11 * 加载项目配置
10 12 * @author: jeecg-boot
11 13 */
12   -@Component("jeeccgBaseConfig")
  14 +@Component("jeecgBaseConfig")
13 15 @ConfigurationProperties(prefix = "jeecg")
14   -public class JeeccgBaseConfig {
  16 +public class JeecgBaseConfig {
  17 + /**
  18 + * 签名密钥串(字典等敏感接口)
  19 + * @TODO 降低使用成本加的默认值,实际以 yml配置 为准
  20 + */
  21 + private String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
  22 +
15 23 /**
16 24 * 是否启用安全模式
17 25 */
... ... @@ -21,10 +29,16 @@ public class JeeccgBaseConfig {
21 29 */
22 30 private Shiro shiro;
23 31 /**
24   - * 签名密钥串(字典等敏感接口)
25   - * @TODO 降低使用成本加的默认值,实际以 yml配置 为准
  32 + * 上传文件配置
26 33 */
27   - private String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
  34 + private Path path;
  35 +
  36 + /**
  37 + * 前端页面访问地址
  38 + * pc: http://localhost:3100
  39 + * app: http://localhost:8051
  40 + */
  41 + private DomainUrl domainUrl;
28 42  
29 43 public Boolean getSafeMode() {
30 44 return safeMode;
... ... @@ -49,4 +63,20 @@ public class JeeccgBaseConfig {
49 63 public void setShiro(Shiro shiro) {
50 64 this.shiro = shiro;
51 65 }
  66 +
  67 + public Path getPath() {
  68 + return path;
  69 + }
  70 +
  71 + public void setPath(Path path) {
  72 + this.path = path;
  73 + }
  74 +
  75 + public DomainUrl getDomainUrl() {
  76 + return domainUrl;
  77 + }
  78 +
  79 + public void setDomainUrl(DomainUrl domainUrl) {
  80 + this.domainUrl = domainUrl;
  81 + }
52 82 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java
... ... @@ -145,4 +145,13 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
145 145 return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
146 146 }
147 147  
  148 +// /**
  149 +// * 注册拦截器【拦截器拦截参数,自动切换数据源——后期实现多租户切换数据源功能】
  150 +// * @param registry
  151 +// */
  152 +// @Override
  153 +// public void addInterceptors(InterceptorRegistry registry) {
  154 +// registry.addInterceptor(new DynamicDatasourceInterceptor()).addPathPatterns("/test/dynamic/**");
  155 +// }
  156 +
148 157 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java
... ... @@ -3,6 +3,9 @@ package org.jeecg.config.mybatis;
3 3 import java.util.ArrayList;
4 4 import java.util.List;
5 5  
  6 +import cn.hutool.core.util.ObjectUtil;
  7 +import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
  8 +import org.jeecg.common.constant.CommonConstant;
6 9 import org.jeecg.common.util.oConvertUtils;
7 10 import org.mybatis.spring.annotation.MapperScan;
8 11 import org.springframework.context.annotation.Bean;
... ... @@ -71,9 +74,35 @@ public class MybatisPlusSaasConfig {
71 74 }
72 75 }));
73 76 interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
  77 + //update-begin-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
  78 + interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
  79 + //update-end-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
74 80 return interceptor;
75 81 }
76 82  
  83 + /**
  84 + * 动态表名切换拦截器,用于适配vue2和vue3同一个表有多个的情况,如sys_role_index在vue3情况下表名为sys_role_index_v3
  85 + * @return
  86 + */
  87 + private DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
  88 + DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
  89 + dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
  90 + //获取需要动态解析的表名
  91 + String dynamicTableName = ThreadLocalDataHelper.get(CommonConstant.DYNAMIC_TABLE_NAME);
  92 + //当dynamicTableName不为空时才走动态表名处理逻辑,否则返回原始表名
  93 + if (ObjectUtil.isNotEmpty(dynamicTableName) && dynamicTableName.equals(tableName)) {
  94 + // 获取前端传递的版本号标识
  95 + Object version = ThreadLocalDataHelper.get(CommonConstant.VERSION);
  96 + if (ObjectUtil.isNotEmpty(version)) {
  97 + //拼接表名规则(原始表名+下划线+前端传递的版本号)
  98 + return tableName + "_" + version;
  99 + }
  100 + }
  101 + return tableName;
  102 + });
  103 + return dynamicTableNameInnerInterceptor;
  104 + }
  105 +
77 106 // /**
78 107 // * 下个版本会删除,现在为了避免缓存出现问题不得不配置
79 108 // * @return
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/ThreadLocalDataHelper.java 0 → 100644
  1 +package org.jeecg.config.mybatis;
  2 +
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
  5 +import java.util.concurrent.ConcurrentHashMap;
  6 +
  7 +
  8 +/**
  9 + * @Description: 本地线程变量存储工具类
  10 + * @author: lsq
  11 + * @date: 2022年03月25日 11:42
  12 + */
  13 +public class ThreadLocalDataHelper {
  14 + /**
  15 + * 线程的本地变量
  16 + */
  17 + private static final ThreadLocal<ConcurrentHashMap> REQUEST_DATA = new ThreadLocal<>();
  18 +
  19 + /**
  20 + * 存储本地参数
  21 + */
  22 + private static final ConcurrentHashMap DATA_MAP = new ConcurrentHashMap<>();
  23 +
  24 + /**
  25 + * 设置请求参数
  26 + *
  27 + * @param key 参数key
  28 + * @param value 参数值
  29 + */
  30 + public static void put(String key, Object value) {
  31 + if(ObjectUtil.isNotEmpty(value)) {
  32 + DATA_MAP.put(key, value);
  33 + REQUEST_DATA.set(DATA_MAP);
  34 + }
  35 + }
  36 +
  37 + /**
  38 + * 获取请求参数值
  39 + *
  40 + * @param key 请求参数
  41 + * @return
  42 + */
  43 + public static <T> T get(String key) {
  44 + ConcurrentHashMap dataMap = REQUEST_DATA.get();
  45 + if (CollectionUtils.isNotEmpty(dataMap)) {
  46 + return (T) dataMap.get(key);
  47 + }
  48 + return null;
  49 + }
  50 +
  51 + /**
  52 + * 获取请求参数
  53 + *
  54 + * @return 请求参数 MAP 对象
  55 + */
  56 + public static void clear() {
  57 + DATA_MAP.clear();
  58 + REQUEST_DATA.remove();
  59 + }
  60 +
  61 +}
  62 +
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/aspect/DynamicTableAspect.java 0 → 100644
  1 +package org.jeecg.config.mybatis.aspect;
  2 +
  3 +import org.aspectj.lang.ProceedingJoinPoint;
  4 +import org.aspectj.lang.annotation.Around;
  5 +import org.aspectj.lang.annotation.Aspect;
  6 +import org.aspectj.lang.annotation.Pointcut;
  7 +import org.aspectj.lang.reflect.MethodSignature;
  8 +import org.jeecg.common.aspect.annotation.DynamicTable;
  9 +import org.jeecg.common.constant.CommonConstant;
  10 +import org.jeecg.common.util.SpringContextUtils;
  11 +import org.jeecg.config.mybatis.ThreadLocalDataHelper;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +import javax.servlet.http.HttpServletRequest;
  15 +import java.lang.reflect.Method;
  16 +
  17 +/**
  18 + * 动态table切换 切面处理
  19 + *
  20 + * @author :zyf
  21 + * @date:2020-04-25
  22 + */
  23 +@Aspect
  24 +@Component
  25 +public class DynamicTableAspect {
  26 +
  27 +
  28 + /**
  29 + * 定义切面拦截切入点
  30 + */
  31 + @Pointcut("@annotation(org.jeecg.common.aspect.annotation.DynamicTable)")
  32 + public void dynamicTable() {
  33 + }
  34 +
  35 +
  36 + @Around("dynamicTable()")
  37 + public Object around(ProceedingJoinPoint point) throws Throwable {
  38 + MethodSignature signature = (MethodSignature) point.getSignature();
  39 + Method method = signature.getMethod();
  40 + DynamicTable dynamicTable = method.getAnnotation(DynamicTable.class);
  41 + HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
  42 + //获取前端传递的版本标记
  43 + String version = request.getHeader(CommonConstant.VERSION);
  44 + //存储版本号到本地线程变量
  45 + ThreadLocalDataHelper.put(CommonConstant.VERSION, version);
  46 + //存储表名到本地线程变量
  47 + ThreadLocalDataHelper.put(CommonConstant.DYNAMIC_TABLE_NAME, dynamicTable.value());
  48 + //执行方法
  49 + Object result = point.proceed();
  50 + //清空本地变量
  51 + ThreadLocalDataHelper.clear();
  52 + return result;
  53 + }
  54 +
  55 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java 0 → 100644
  1 +package org.jeecg.config.mybatis.interceptor;
  2 +
  3 +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.apache.commons.lang3.StringUtils;
  6 +import org.springframework.web.servlet.HandlerInterceptor;
  7 +import org.springframework.web.servlet.ModelAndView;
  8 +
  9 +import javax.servlet.http.HttpServletRequest;
  10 +import javax.servlet.http.HttpServletResponse;
  11 +
  12 +/**
  13 + * 动态数据源切换拦截器
  14 + *
  15 + * 测试:拦截参数,自动切换数据源
  16 + * 未来规划:后面通过此机制,实现多租户切换数据源功能
  17 + * @author zyf
  18 + */
  19 +@Slf4j
  20 +public class DynamicDatasourceInterceptor implements HandlerInterceptor {
  21 +
  22 + /**
  23 + * 在请求处理之前进行调用(Controller方法调用之前)
  24 + */
  25 + @Override
  26 + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
  27 + String requestURI = request.getRequestURI();
  28 + log.info("经过多数据源Interceptor,当前路径是{}", requestURI);
  29 + //获取动态数据源名称
  30 + String dsName = request.getParameter("dsName");
  31 + String dsKey = "master";
  32 + if (StringUtils.isNotEmpty(dsName)) {
  33 + dsKey = dsName;
  34 + }
  35 + DynamicDataSourceContextHolder.push(dsKey);
  36 + return true;
  37 + }
  38 +
  39 + /**
  40 + * 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
  41 + */
  42 + @Override
  43 + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
  44 +
  45 + }
  46 +
  47 + /**
  48 + * 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
  49 + */
  50 + @Override
  51 + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
  52 + DynamicDataSourceContextHolder.clear();
  53 + }
  54 +
  55 +}
0 56 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java
... ... @@ -15,7 +15,7 @@ import org.crazycake.shiro.RedisClusterManager;
15 15 import org.crazycake.shiro.RedisManager;
16 16 import org.jeecg.common.constant.CommonConstant;
17 17 import org.jeecg.common.util.oConvertUtils;
18   -import org.jeecg.config.JeeccgBaseConfig;
  18 +import org.jeecg.config.JeecgBaseConfig;
19 19 import org.jeecg.config.shiro.filters.CustomShiroFilterFactoryBean;
20 20 import org.jeecg.config.shiro.filters.JwtFilter;
21 21 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
... ... @@ -45,11 +45,11 @@ import java.util.*;
45 45 public class ShiroConfig {
46 46  
47 47 @Resource
48   - LettuceConnectionFactory lettuceConnectionFactory;
  48 + private LettuceConnectionFactory lettuceConnectionFactory;
49 49 @Autowired
50 50 private Environment env;
51   - @Autowired
52   - JeeccgBaseConfig jeeccgBaseConfig;
  51 + @Resource
  52 + private JeecgBaseConfig jeecgBaseConfig;
53 53  
54 54 /**
55 55 * Filter Chain定义说明
... ... @@ -64,11 +64,15 @@ public class ShiroConfig {
64 64 shiroFilterFactoryBean.setSecurityManager(securityManager);
65 65 // 拦截器
66 66 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
67   - String shiroExcludeUrls = jeeccgBaseConfig.getShiro().getExcludeUrls();
68   - if(oConvertUtils.isNotEmpty(shiroExcludeUrls)){
69   - String[] permissionUrl = shiroExcludeUrls.split(",");
70   - for(String url : permissionUrl){
71   - filterChainDefinitionMap.put(url,"anon");
  67 +
  68 + //支持yml方式,配置拦截排除
  69 + if(jeecgBaseConfig.getShiro()!=null){
  70 + String shiroExcludeUrls = jeecgBaseConfig.getShiro().getExcludeUrls();
  71 + if(oConvertUtils.isNotEmpty(shiroExcludeUrls)){
  72 + String[] permissionUrl = shiroExcludeUrls.split(",");
  73 + for(String url : permissionUrl){
  74 + filterChainDefinitionMap.put(url,"anon");
  75 + }
72 76 }
73 77 }
74 78 // 配置不会被拦截的链接 顺序判断
... ... @@ -125,9 +129,11 @@ public class ShiroConfig {
125 129 filterChainDefinitionMap.put("/jmreport/**", "anon");
126 130 filterChainDefinitionMap.put("/**/*.js.map", "anon");
127 131 filterChainDefinitionMap.put("/**/*.css.map", "anon");
128   -
129   - //测试示例
130   - filterChainDefinitionMap.put("/test/bigScreen/**", "anon"); //大屏模板例子
  132 +
  133 + //大屏模板例子
  134 + filterChainDefinitionMap.put("/test/bigScreen/**", "anon");
  135 + filterChainDefinitionMap.put("/bigscreen/template1/**", "anon");
  136 + filterChainDefinitionMap.put("/bigscreen/template1/**", "anon");
131 137 //filterChainDefinitionMap.put("/test/jeecgDemo/rabbitMqClientTest/**", "anon"); //MQ测试
132 138 //filterChainDefinitionMap.put("/test/jeecgDemo/html", "anon"); //模板页面
133 139 //filterChainDefinitionMap.put("/test/jeecgDemo/redis/**", "anon"); //redis测试
... ... @@ -137,8 +143,6 @@ public class ShiroConfig {
137 143 filterChainDefinitionMap.put("/newsWebsocket/**", "anon");//CMS模块
138 144 filterChainDefinitionMap.put("/vxeSocket/**", "anon");//JVxeTable无痕刷新示例
139 145  
140   - //wps
141   - filterChainDefinitionMap.put("/v1/**","anon");
142 146  
143 147 //性能监控 TODO 存在安全漏洞泄露TOEKN(durid连接池也有)
144 148 filterChainDefinitionMap.put("/actuator/**", "anon");
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java
... ... @@ -69,13 +69,13 @@ public class ShiroRealm extends AuthorizingRealm {
69 69  
70 70 // 设置用户拥有的角色集合,比如“admin,test”
71 71 Set<String> roleSet = commonApi.queryUserRoles(username);
72   - System.out.println(roleSet.toString());
  72 + //System.out.println(roleSet.toString());
73 73 info.setRoles(roleSet);
74 74  
75 75 // 设置用户拥有的权限集合,比如“sys:role:add,sys:user:add”
76 76 Set<String> permissionSet = commonApi.queryUserAuths(username);
77 77 info.addStringPermissions(permissionSet);
78   - System.out.println(permissionSet);
  78 + //System.out.println(permissionSet);
79 79 log.info("===============Shiro权限认证成功==============");
80 80 return info;
81 81 }
... ... @@ -123,7 +123,7 @@ public class ShiroRealm extends AuthorizingRealm {
123 123  
124 124 // 查询用户信息
125 125 log.debug("———校验token是否有效————checkUserTokenIsEffect——————— "+ token);
126   - LoginUser loginUser = TokenUtils.getLoginUser(username,commonApi,redisUtil);
  126 + LoginUser loginUser = TokenUtils.getLoginUser(username, commonApi, redisUtil);
127 127 //LoginUser loginUser = commonApi.getUserByName(username);
128 128 if (loginUser == null) {
129 129 throw new AuthenticationException("用户不存在!");
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/filters/JwtFilter.java
... ... @@ -107,4 +107,18 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
107 107  
108 108 return super.preHandle(request, response);
109 109 }
  110 +
  111 + /**
  112 + * JwtFilter中ThreadLocal需要及时清除 #3634
  113 + *
  114 + * @param request
  115 + * @param response
  116 + * @param exception
  117 + * @throws Exception
  118 + */
  119 + @Override
  120 + public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
  121 + //log.info("------清空线程中多租户的ID={}------",TenantContext.getTenant());
  122 + TenantContext.clear();
  123 + }
110 124 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
1 1 package org.jeecg.config.sign.interceptor;
2 2  
  3 +import org.jeecg.config.filter.RequestBodyReserveFilter;
  4 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
3 5 import org.springframework.context.annotation.Bean;
4 6 import org.springframework.context.annotation.Configuration;
5 7 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
... ... @@ -24,4 +26,22 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
24 26 public void addInterceptors(InterceptorRegistry registry) {
25 27 registry.addInterceptor(signAuthInterceptor()).addPathPatterns(SIGN_URL_LIST);
26 28 }
  29 +
  30 + //update-begin-author:taoyan date:20220427 for: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空
  31 + @Bean
  32 + public RequestBodyReserveFilter requestBodyReserveFilter(){
  33 + return new RequestBodyReserveFilter();
  34 + }
  35 +
  36 + @Bean
  37 + public FilterRegistrationBean reqBodyFilterRegistrationBean(){
  38 + FilterRegistrationBean registration = new FilterRegistrationBean();
  39 + registration.setFilter(requestBodyReserveFilter());
  40 + registration.setName("requestBodyReserveFilter");
  41 + // 建议此处只添加post请求地址而不是所有的都需要走过滤器
  42 + registration.addUrlPatterns(SIGN_URL_LIST);
  43 + return registration;
  44 + }
  45 + //update-end-author:taoyan date:20220427 for: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空
  46 +
27 47 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
... ... @@ -32,7 +32,7 @@ public class SignAuthInterceptor implements HandlerInterceptor {
32 32  
33 33 @Override
34 34 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
35   - log.info("request URI = " + request.getRequestURI());
  35 + log.info("Sign Interceptor request URI = " + request.getRequestURI());
36 36 HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
37 37 //获取全部参数(包括URL和body上的)
38 38 SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java
... ... @@ -6,7 +6,7 @@ import org.jeecg.common.constant.SymbolConstant;
6 6 import org.jeecg.common.exception.JeecgBootException;
7 7 import org.jeecg.common.util.SpringContextUtils;
8 8 import org.jeecg.common.util.oConvertUtils;
9   -import org.jeecg.config.JeeccgBaseConfig;
  9 +import org.jeecg.config.JeecgBaseConfig;
10 10 import org.springframework.util.DigestUtils;
11 11 import org.springframework.util.StringUtils;
12 12  
... ... @@ -48,8 +48,8 @@ public class SignUtil {
48 48 String paramsJsonStr = JSONObject.toJSONString(params);
49 49 log.info("Param paramsJsonStr : {}", paramsJsonStr);
50 50 //设置签名秘钥
51   - JeeccgBaseConfig jeeccgBaseConfig = SpringContextUtils.getBean(JeeccgBaseConfig.class);
52   - String signatureSecret = jeeccgBaseConfig.getSignatureSecret();
  51 + JeecgBaseConfig jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
  52 + String signatureSecret = jeecgBaseConfig.getSignatureSecret();
53 53 String curlyBracket = SymbolConstant.DOLLAR + SymbolConstant.LEFT_CURLY_BRACKET;
54 54 if(oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains(curlyBracket)){
55 55 throw new JeecgBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 !!");
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/vo/DomainUrl.java 0 → 100644
  1 +package org.jeecg.config.vo;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * @Author taoYan
  7 + * @Date 2022/7/5 21:16
  8 + **/
  9 +@Data
  10 +public class DomainUrl {
  11 +
  12 + private String pc;
  13 +
  14 + private String app;
  15 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/vo/Path.java 0 → 100644
  1 +package org.jeecg.config.vo;
  2 +
  3 +import javax.print.DocFlavor;
  4 +
  5 +/**
  6 + *
  7 + * @author: scott
  8 + * @date: 2022年04月18日 20:35
  9 + */
  10 +public class Path {
  11 + private String upload;
  12 + private String webapp;
  13 +
  14 + public String getUpload() {
  15 + return upload;
  16 + }
  17 +
  18 + public void setUpload(String upload) {
  19 + this.upload = upload;
  20 + }
  21 +
  22 + public String getWebapp() {
  23 + return webapp;
  24 + }
  25 +
  26 + public void setWebapp(String webapp) {
  27 + this.webapp = webapp;
  28 + }
  29 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/xml/BaseCommonMapper.xml
... ... @@ -4,19 +4,22 @@
4 4  
5 5 <!-- 保存日志11 -->
6 6 <insert id="saveLog" parameterType="Object">
7   - insert into sys_log (id, log_type, log_content, method, operate_type, request_param, ip, userid, username, cost_time, create_time)
  7 + insert into sys_log (id, log_type, log_content, method, operate_type, request_url, request_type, request_param, ip, userid, username, cost_time, create_time,create_by)
8 8 values(
9 9 #{dto.id,jdbcType=VARCHAR},
10 10 #{dto.logType,jdbcType=INTEGER},
11 11 #{dto.logContent,jdbcType=VARCHAR},
12 12 #{dto.method,jdbcType=VARCHAR},
13 13 #{dto.operateType,jdbcType=INTEGER},
  14 + #{dto.requestUrl,jdbcType=VARCHAR},
  15 + #{dto.requestType,jdbcType=VARCHAR},
14 16 #{dto.requestParam,jdbcType=VARCHAR},
15 17 #{dto.ip,jdbcType=VARCHAR},
16 18 #{dto.userid,jdbcType=VARCHAR},
17 19 #{dto.username,jdbcType=VARCHAR},
18 20 #{dto.costTime,jdbcType=BIGINT},
19   - #{dto.createTime,jdbcType=TIMESTAMP}
  21 + #{dto.createTime,jdbcType=TIMESTAMP},
  22 + #{dto.createBy,jdbcType=VARCHAR}
20 23 )
21 24 </insert>
22 25  
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/service/impl/BaseCommonServiceImpl.java
... ... @@ -34,6 +34,7 @@ public class BaseCommonServiceImpl implements BaseCommonService {
34 34 }
35 35 //保存日志(异常捕获处理,防止数据太大存储失败,导致业务失败)JT-238
36 36 try {
  37 + logDTO.setCreateTime(new Date());
37 38 baseCommonMapper.saveLog(logDTO);
38 39 } catch (Exception e) {
39 40 log.warn(" LogContent length : "+logDTO.getLogContent().length());
... ...