智慧保安后台管理-外网项目备份
1. 登录记录新增
2. 用户6个月,1年未登录定时任务新增
3. 脱敏 aop 注解新增
9 files modified
4 files added
409 ■■■■■ changed files
pom.xml 6 ●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/desensitization/Desensitization.java 28 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/desensitization/DesensitizationAspect.java 143 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/desensitization/DesensitizationWord.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/quartz/task/Task.java 23 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/system/mapper/UserMapper.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/system/mapper/UserMapper.xml 43 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/system/service/IUserService.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java 22 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/test/TestController.java 75 ●●●●● patch | view | raw | blame | history
src/main/resources/application-test.yml 6 ●●●● patch | view | raw | blame | history
src/main/resources/application.yml 1 ●●●● patch | view | raw | blame | history
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>
src/main/java/org/springblade/common/desensitization/Desensitization.java
New file
@@ -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;
}
src/main/java/org/springblade/common/desensitization/DesensitizationAspect.java
New file
@@ -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);
            }
        }
    }
}
src/main/java/org/springblade/common/desensitization/DesensitizationWord.java
New file
@@ -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();
}
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);
    }
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();
    }
}
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);
}
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>
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();
}
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);
    }
}
src/main/java/org/springblade/modules/test/TestController.java
New file
@@ -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);
    }
}
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:
src/main/resources/application.yml
@@ -195,6 +195,7 @@
  secure:
    #接口放行
    skip-url:
      - /test/**
      - /blade-test/**
      - /blade-system/dept/selectInfo
      - /blade-user/zc