From 0c820746a96a1193a64532b3b17b08d52e4ff1be Mon Sep 17 00:00:00 2001
From: zhongrj <646384940@qq.com>
Date: Mon, 11 Sep 2023 19:13:15 +0800
Subject: [PATCH] 1. 登录记录新增 2. 用户6个月,1年未登录定时任务新增 3. 脱敏 aop 注解新增

---
 src/main/java/org/springblade/modules/system/mapper/UserMapper.xml              |   43 ++++++
 src/main/java/org/springblade/common/desensitization/Desensitization.java       |   28 ++++
 src/main/resources/application-test.yml                                         |    6 
 pom.xml                                                                         |    6 
 src/main/java/org/springblade/modules/quartz/task/Task.java                     |   23 +++
 src/main/java/org/springblade/modules/test/TestController.java                  |   75 ++++++++++
 src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java     |   16 +
 src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java  |   22 +++
 src/main/java/org/springblade/common/desensitization/DesensitizationWord.java   |   16 ++
 src/main/java/org/springblade/common/desensitization/DesensitizationAspect.java |  143 ++++++++++++++++++++
 src/main/java/org/springblade/modules/system/mapper/UserMapper.java             |   20 ++
 src/main/java/org/springblade/modules/system/service/IUserService.java          |   10 +
 src/main/resources/application.yml                                              |    1 
 13 files changed, 397 insertions(+), 12 deletions(-)

diff --git a/pom.xml b/pom.xml
index caf2bcc..c58de87 100644
--- a/pom.xml
+++ b/pom.xml
@@ -294,7 +294,11 @@
             <groupId>redis.clients</groupId>
             <artifactId>jedis</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.7.16</version>
+        </dependency>
     </dependencies>
     <profiles>
         <profile>
diff --git a/src/main/java/org/springblade/common/desensitization/Desensitization.java b/src/main/java/org/springblade/common/desensitization/Desensitization.java
new file mode 100644
index 0000000..16e5222
--- /dev/null
+++ b/src/main/java/org/springblade/common/desensitization/Desensitization.java
@@ -0,0 +1,28 @@
+package org.springblade.common.desensitization;
+
+import cn.hutool.core.util.DesensitizedUtil;
+
+import java.lang.annotation.*;
+
+import static cn.hutool.core.util.DesensitizedUtil.DesensitizedType.FIXED_PHONE;
+
+/**
+ * 脱敏
+ * @author zhongrj
+ * @date 2023-09-11
+ */
+public @interface Desensitization {
+
+	/**
+	 * json path 的标识
+	 * @return
+	 */
+	String jsonPath();
+
+	/**
+	 * 脱敏的字段的数据分类,  默认是座机号码类型脱敏
+	 * @return
+	 */
+	DesensitizedUtil.DesensitizedType desensitizedType() default FIXED_PHONE;
+
+}
diff --git a/src/main/java/org/springblade/common/desensitization/DesensitizationAspect.java b/src/main/java/org/springblade/common/desensitization/DesensitizationAspect.java
new file mode 100644
index 0000000..d9ea963
--- /dev/null
+++ b/src/main/java/org/springblade/common/desensitization/DesensitizationAspect.java
@@ -0,0 +1,143 @@
+package org.springblade.common.desensitization;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.DesensitizedUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONPath;
+import liquibase.util.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springframework.stereotype.Component;
+
+import static cn.hutool.core.util.DesensitizedUtil.DesensitizedType.FIXED_PHONE;
+
+/**
+ * 数据脱敏切面
+ * @author zhongrj
+ * @date 2023-09-11
+ */
+@Slf4j
+@Aspect
+@Component
+public class DesensitizationAspect {
+
+	/**
+	 * 脱敏环绕通知
+	 * @param pjp
+	 * @param desensitizationWord
+	 * @return
+	 * @throws Throwable
+	 */
+	@Around("@annotation(desensitizationWord)")
+	public Object around(ProceedingJoinPoint pjp, DesensitizationWord desensitizationWord) throws Throwable{
+		Object object = pjp.proceed();
+		// 判断是否需要脱敏处理
+		if (isDesensitization()) {
+			try {
+				Class<?> aClass = object.getClass();
+				// 改变返回值
+				Desensitization[] value = desensitizationWord.value();
+				JSONObject ob = JSON.parseObject(JSON.toJSONString(object));
+
+				// 循环处理每一个 json 路径下的值
+				for (Desensitization s : value) {
+					replace(ob, s);
+				}
+				object = JSON.parseObject(JSON.toJSONString(ob), aClass);
+			} catch (Exception e) {
+				log.info("数据脱敏失败:" + e.getMessage());
+			}
+		}
+		return object;
+	}
+
+	/**
+	 * 判断是否需要脱敏
+	 * @return
+	 */
+	private boolean isDesensitization() {
+		// 获取用户角色,根据角色判断是否进行脱敏处理
+		String userRole = AuthUtil.getUserRole();
+		// 如果用户角色为null,则需要脱敏
+		if (null!=userRole && !userRole.equals("")){
+			// 判断角色是否为民警角色,如果是则不需要脱敏,否则需要脱敏处理
+			if (userRole.equals("民警")){
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * 脱敏
+	 */
+	private Object sensitiveByString(Object value) {
+		if (StringUtils.isNotEmpty(value.toString())) {
+			String st = Convert.toStr(value);
+			st = st.substring(0, st.length() - 3 > 0 ? 3 : st.length()) + "****" + st.substring(Math.max(st.length() - 4, 0));
+			return st;
+		}
+		return value;
+	}
+
+	/**
+	 * 敏感数据替换
+	 *
+	 * @param jsonObject
+	 * @param s
+	 */
+	private void replace(JSONObject jsonObject, Desensitization s) {
+		// 只有传入的 JSON 路径在 这个 JSONObject 中才会进行脱敏处理
+		if (JSONPath.contains(jsonObject, s.jsonPath())) {
+
+			// 查询是否有数组 列表 类型的数据需要脱敏
+			int index = s.jsonPath().lastIndexOf("[*]");
+			if (index > -1) {
+				String prefix = StrUtil.subPre(s.jsonPath(), index);
+				String suffix = StrUtil.subSuf(s.jsonPath(), index + 3);
+				// 提取json 路径下的 数组\链表 元素
+				Object eval = JSONPath.eval(jsonObject, prefix);
+
+				// 将数组\链表 元素 转为 JSONArray 方便做 统一格式处理
+				JSONArray jsonArray = (JSONArray) eval;
+				int size = jsonArray.size();
+				for (int i = 0; i < size; i++) {
+					// 由于脱敏数组内部的参数传入格式为 :jsonPath = "$.data.records[*].username"
+					// 所以需要重新组装 jsonPath 将 * 号 替换成具体的值
+					String indexJsonPath = StrUtil.strBuilder().append(prefix).append("[").append(i).append("]").append(suffix).toString();
+					// 使用 cn.hutool.core.convert Convert.toStr 转换为字符串 如果给定的值为null,或者转换失败,返回默认值null,这样可以减少报错,避免程序异常
+					String desensitized = Convert.toStr(JSONPath.eval(jsonObject, indexJsonPath));
+					if (StrUtil.isBlank(desensitized)) {
+						continue;
+					}
+					// 如果是默认指定 则使用默认方式脱敏
+					if (s.desensitizedType() == FIXED_PHONE) {
+						desensitized = sensitiveByString(desensitized).toString();
+					}else {
+						// 否则使用 cn.hutool.core.util 进行数据脱敏
+						desensitized = DesensitizedUtil.desensitized(desensitized, s.desensitizedType());
+					}
+
+					// 使用JSON 路径操作,将已经脱敏的新数据,放入之前未脱敏的数据地址处,替换未脱敏数据
+					JSONPath.set(jsonObject, indexJsonPath, desensitized);
+				}
+			} else {
+				// 使用 cn.hutool.core.convert Convert.toStr 转换为字符串 如果给定的值为null,或者转换失败,返回默认值null,这样可以减少报错,避免程序异常
+				Object eval = JSONPath.eval(jsonObject, Convert.toStr(s.jsonPath()));
+				String desensitized = "";
+				if (s.desensitizedType() == FIXED_PHONE) {
+					desensitized = sensitiveByString(Convert.toStr(eval)).toString();
+				}else {
+					desensitized = DesensitizedUtil.desensitized(Convert.toStr(eval), s.desensitizedType());
+				}
+				JSONPath.set(jsonObject, s.jsonPath(), desensitized);
+			}
+		}
+	}
+}
diff --git a/src/main/java/org/springblade/common/desensitization/DesensitizationWord.java b/src/main/java/org/springblade/common/desensitization/DesensitizationWord.java
new file mode 100644
index 0000000..d8b064a
--- /dev/null
+++ b/src/main/java/org/springblade/common/desensitization/DesensitizationWord.java
@@ -0,0 +1,16 @@
+package org.springblade.common.desensitization;
+
+import java.lang.annotation.*;
+
+/**
+ * 脱敏
+ * @author zhongrj
+ * @date 2023-09-11
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DesensitizationWord {
+
+	Desensitization [] value();
+}
diff --git a/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java b/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java
index b0f4db2..5c9cafc 100644
--- a/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java
+++ b/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java
@@ -158,13 +158,17 @@
 					}
 				}
 			}
-		}else {
-			System.out.println("grantType = " + grantType);
 			//刷新 token 不新增登录记录
-//			if (!grantType.equals("refresh_token")){
-//				//新增登录记录
-//				this.saveLoginRecord(userInfo);
-//			}
+			if (!grantType.equals("refresh_token")){
+				//新增登录记录
+				this.saveLoginRecord(userInfo);
+			}
+		}else {
+			//刷新 token 不新增登录记录
+			if (!grantType.equals("refresh_token")){
+				//新增登录记录
+				this.saveLoginRecord(userInfo);
+			}
 		}
 		return TokenUtil.createAuthInfo(userInfo);
 	}
diff --git a/src/main/java/org/springblade/modules/quartz/task/Task.java b/src/main/java/org/springblade/modules/quartz/task/Task.java
index e3eaa2e..073a9cf 100644
--- a/src/main/java/org/springblade/modules/quartz/task/Task.java
+++ b/src/main/java/org/springblade/modules/quartz/task/Task.java
@@ -1,5 +1,6 @@
 package org.springblade.modules.quartz.task;
 
+import org.springblade.modules.system.service.IUserService;
 import org.springblade.modules.training.service.TrainingRegistrationService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
@@ -20,6 +21,9 @@
 	@Autowired
 	private TrainingRegistrationService trainingRegistrationService;
 
+	@Autowired
+	private IUserService userService;
+
 	public void testTask(){
         System.out.println("测试定时任务执行-----------------");
     }
@@ -38,11 +42,26 @@
 	/**
 	 * 定时任务,处理考试中的人员
 	 */
-//	@Scheduled(cron = "0 0 22 * * ?")
 	public void examLoading(){
-		System.out.println("定时任务2:执行时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+		System.out.println("定时任务,处理考试中的人员:执行时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
 		//自动处理之前报了名,考试忘记提交或者中断考试的
 		trainingRegistrationService.examLoading();
 	}
 
+	/**
+	 * 定时任务,6个月未登录人员,进行冻结
+	 */
+	public void sixMonthNotLoginHandle(){
+		System.out.println("定时任务,6个月未登录人员,进行冻结:执行时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+		userService.sixMonthNotLoginHandle();
+	}
+
+	/**
+	 * 定时任务,12个月未登录人员,进行注销
+	 */
+	public void oneYearNotLoginHandle(){
+		System.out.println("定时任务,12个月未登录人员,进行注销:执行时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+		userService.oneYearNotLoginHandle();
+	}
+
 }
diff --git a/src/main/java/org/springblade/modules/system/mapper/UserMapper.java b/src/main/java/org/springblade/modules/system/mapper/UserMapper.java
index 5d05630..492f1a1 100644
--- a/src/main/java/org/springblade/modules/system/mapper/UserMapper.java
+++ b/src/main/java/org/springblade/modules/system/mapper/UserMapper.java
@@ -279,4 +279,24 @@
 	 * @return
 	 */
 	User getUserByPhone(@Param("tenantId")String tenantId,@Param("phone") String phone);
+
+	/**
+	 * 按天查询 day 天未登录的人员ids集合信息
+	 * @param day
+	 * @return
+	 */
+    List<String> getMoreDayNotLoginUser(@Param("day")int day);
+
+	/**
+	 * 用户冻结操作
+	 * @param userIds
+	 */
+	void updateFreeze(@Param("list") List<String> userIds);
+
+	/**
+	 * 用户注销操作
+	 * @param userIds
+	 */
+	void updateLogout(@Param("list") List<String> userIds);
+
 }
diff --git a/src/main/java/org/springblade/modules/system/mapper/UserMapper.xml b/src/main/java/org/springblade/modules/system/mapper/UserMapper.xml
index 6b228c0..4ee38f8 100644
--- a/src/main/java/org/springblade/modules/system/mapper/UserMapper.xml
+++ b/src/main/java/org/springblade/modules/system/mapper/UserMapper.xml
@@ -1144,5 +1144,48 @@
         and tenant_id = #{tenantId} and phone = #{phone}
     </select>
 
+    <!--按天查询 day 天未登录的人员ids集合信息-->
+    <select id="getMoreDayNotLoginUser" resultType="java.lang.String">
+        select bu.id from blade_user bu
+        left join  (
+            select user_id,max(create_time) as create_time from sys_login_record GROUP BY user_id
+        )slr on bu.id = slr.user_id
+         where bu.status = 1 and bu.is_deleted = 0 and bu.role_id = 1412226235153731586
+         and DATE_SUB(CURDATE(), INTERVAL #{day} DAY) > date(bu.create_time)
+         and DATE_SUB(CURDATE(), INTERVAL #{day} DAY) > date(slr.create_time)
+    </select>
+
+    <!--用户冻结操作-->
+    <update id="updateFreeze">
+        update blade_user set is_deleted = 1
+        <choose>
+            <when test="list!=null and list.size()>0">
+                where id in
+                <foreach collection="list" item="id" open="(" close=")" separator=",">
+                    #{id}
+                </foreach>
+            </when>
+            <otherwise>
+                where id in ('')
+            </otherwise>
+        </choose>
+    </update>
+
+    <!--用户注销操作-->
+    <update id="updateLogout">
+        update blade_user set is_deleted = 1
+        <choose>
+            <when test="list!=null and list.size()>0">
+                where id in
+                <foreach collection="list" item="id" open="(" close=")" separator=",">
+                    #{id}
+                </foreach>
+            </when>
+            <otherwise>
+                where id in ('')
+            </otherwise>
+        </choose>
+    </update>
+
 
 </mapper>
diff --git a/src/main/java/org/springblade/modules/system/service/IUserService.java b/src/main/java/org/springblade/modules/system/service/IUserService.java
index 87a24bc..7e05f7f 100644
--- a/src/main/java/org/springblade/modules/system/service/IUserService.java
+++ b/src/main/java/org/springblade/modules/system/service/IUserService.java
@@ -394,4 +394,14 @@
 	 * @return
 	 */
 	UserInfo userInfoByWx(String tenantId, String phone, UserEnum web);
+
+	/**
+	 * 6个月未登录人员,进行冻结
+	 */
+    void sixMonthNotLoginHandle();
+
+	/**
+	 * 12个月未登录人员,进行注销
+	 */
+	void oneYearNotLoginHandle();
 }
diff --git a/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java b/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java
index 077dd5a..0853aba 100644
--- a/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java
+++ b/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java
@@ -1991,4 +1991,26 @@
 	public User getUserById(String id) {
 		return baseMapper.getUserById(Long.parseLong(id));
 	}
+
+	/**
+	 * 6个月未登录人员,进行冻结
+	 */
+	@Override
+	public void sixMonthNotLoginHandle() {
+		// 查询6个月未登录人员
+		List<String> userIds = baseMapper.getMoreDayNotLoginUser(183);
+		// 统一冻结操作
+		baseMapper.updateFreeze(userIds);
+	}
+
+	/**
+	 * 12个月未登录人员,进行注销
+	 */
+	@Override
+	public void oneYearNotLoginHandle() {
+		// 查询12个月未登录人员
+		List<String> userIds = baseMapper.getMoreDayNotLoginUser(366);
+		// 统一注销操作
+		baseMapper.updateLogout(userIds);
+	}
 }
diff --git a/src/main/java/org/springblade/modules/test/TestController.java b/src/main/java/org/springblade/modules/test/TestController.java
new file mode 100644
index 0000000..8fef2fe
--- /dev/null
+++ b/src/main/java/org/springblade/modules/test/TestController.java
@@ -0,0 +1,75 @@
+package org.springblade.modules.test;
+
+import com.yunpian.sdk.model.Result;
+import lombok.AllArgsConstructor;
+import org.springblade.common.desensitization.Desensitization;
+import org.springblade.common.desensitization.DesensitizationWord;
+import org.springblade.core.tool.api.R;
+import org.springblade.modules.system.entity.User;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static cn.hutool.core.util.DesensitizedUtil.DesensitizedType.*;
+
+@AllArgsConstructor
+@RestController
+@RequestMapping("/test")
+public class TestController {
+
+
+	@GetMapping("/getUserInfo")
+	@DesensitizationWord({
+		@Desensitization(jsonPath = "$.data.name",desensitizedType = CHINESE_NAME),
+		@Desensitization(jsonPath = "$.data.phone",desensitizedType = MOBILE_PHONE),
+		@Desensitization(jsonPath = "$.data.bankCard",desensitizedType = BANK_CARD),
+	})
+	public R getUserInfo() {
+		Map<String, String> map = new HashMap<>();
+		map.put("name","张三");
+		map.put("phone","18111111111");
+		map.put("bankCard","6227112222211111211");
+		return R.data(map);
+	}
+
+	/**
+	 * 对象脱敏
+	 * @return
+	 */
+	@GetMapping("getInfo2")
+	@DesensitizationWord({
+		@Desensitization(jsonPath = "$.data.phone",desensitizedType = MOBILE_PHONE),
+		@Desensitization(jsonPath = "$.data.realName",desensitizedType = CHINESE_NAME),
+	})
+	public R getCaseInfo2() {
+		User user = new User();
+		user.setPhone("18111111111");
+		user.setRealName("cs1");
+		return R.data(user);
+	}
+
+	/**
+	 * 集合脱敏
+	 * @return
+	 */
+	@GetMapping("getInfo3")
+	@DesensitizationWord(@Desensitization(jsonPath = "$.data[*].phone"))
+	public R getCaseInfo(){
+		List<User> users = new ArrayList<>();
+		User user = new User();
+		user.setPhone("18111111111");
+		User user1 = new User();
+		user1.setPhone("18222222222");
+		users.add(user);
+		users.add(user1);
+		return R.data(users);
+	}
+
+
+}
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
index cbe94e4..0d60fa4 100644
--- a/src/main/resources/application-test.yml
+++ b/src/main/resources/application-test.yml
@@ -15,7 +15,7 @@
     # MySql
     url: jdbc:mysql://127.0.0.1:3308/zhbaw_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
     username: root
-    password: 123456
+    password: root
     # rabbitmq 设置
 #  rabbitmq:
 #    host: 192.168.0.191
@@ -79,9 +79,9 @@
     enable: stop
     type: sql
     driver: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://106.225.193.35:3306/zhbaw-test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
+    url: jdbc:mysql://127.0.0.1:3308/zhbaw_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
     username: root
-    password: HCyj@2022
+    password: root
 
 #第三方登陆
 social:
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 951e481..8758f83 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -195,6 +195,7 @@
   secure:
     #接口放行
     skip-url:
+      - /test/**
       - /blade-test/**
       - /blade-system/dept/selectInfo
       - /blade-user/zc

--
Gitblit v1.9.3