whycxzp
2022-03-22 c7966221c3cef9fc77ec81aa9acba23e0bcc8d7d
审计日志,记录分类更新
7个文件已修改
266 ■■■■■ 已修改文件
src/main/java/com/whyc/aop/OperationLogAspect.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/constant/UserOperation.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/exception/CustomExceptionResultHandler.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/service/LoginService.java 154 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/service/UserLogService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/util/ActionUtil.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/util/CommonUtil.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/aop/OperationLogAspect.java
@@ -46,31 +46,41 @@
        //用户id
        Long uId = ActionUtil.getUser().getUId();
        //操作类型:目前记录 增删改/登录登出
        Signature signature = point.getSignature();
        String methodSignature = signature.toString();
        String methodName = signature.getName();
        //执行的类全名
        String fullClassName = signature.getDeclaringTypeName();
        Integer operationType = 0;
        String  operationTypeName = null;
        if(methodName.contains("update")){
            operationTypeName = UserOperation.TYPE_UPDATE.getTypeName();
            operationType = UserOperation.TYPE_UPDATE.getType();
            //提取单项-修改配置
            if(fullClassName.contains("pageParam")){
                operationTypeName = UserOperation.TYPE_PARAM_CHANGE.getTypeName();
                operationType = UserOperation.TYPE_PARAM_CHANGE.getType();
            //提取单项-密码修改
            }else if(fullClassName.contains("updatePassword")){
                operationTypeName = UserOperation.TYPE_PASSWORD_CHANGE.getTypeName();
                operationType = UserOperation.TYPE_PASSWORD_CHANGE.getType();
            }else {
                operationTypeName = UserOperation.TYPE_UPDATE.getTypeName();
                operationType = UserOperation.TYPE_UPDATE.getType();
            }
        }else if (methodName.contains("add")){
            operationTypeName = UserOperation.TYPE_ADD.getTypeName();
            operationType = UserOperation.TYPE_ADD.getType();
        }else if (methodName.contains("delete")){
            operationTypeName = UserOperation.TYPE_DELETE.getTypeName();
            operationType = UserOperation.TYPE_DELETE.getType();
        }else if (methodName.contains("login")){
        }/*else if (methodName.contains("login")){
            operationTypeName = UserOperation.TYPE_LOGIN.getTypeName();
            operationType = UserOperation.TYPE_LOGIN.getType();
        }else if (methodName.contains("logout")){
            operationTypeName = UserOperation.TYPE_LOGOUT.getTypeName();
            operationType = UserOperation.TYPE_LOGOUT.getType();
        }
        }*/
        //执行的类全名
        String fullClassName = signature.getDeclaringTypeName().toString();
        //获取类型
        String[] fullClassNameSplit = fullClassName.split("\\.");
        String className = fullClassNameSplit[fullClassNameSplit.length-1].replace("Controller","模块");
src/main/java/com/whyc/constant/UserOperation.java
@@ -8,12 +8,19 @@
    TYPE_ADD(3,"新增"),
    TYPE_UPDATE(4,"修改"),
    TYPE_DELETE(5,"删除"),
    TYPE_CANCEL(7,"取消"),
    TYPE_STOP(9,"终止"),
    TYPE_UNRECOGNIZED(-1,"无法识别的操作类型"),
    TYPE_UNAUTHORIZED_ACCESS(20,"越权访问"),
    TYPE_EXCEPTION(21,"调用异常"),
    /**操作分类 TODO,不确定会使用*/
    SORT_LOGIN(1000,"登录"),
    SORT_COMMON(1001,"常规操作");
    /**追加的系统级操作类型*/
    TYPE_LOGIN_FAIL(31,"登录失败"),
    TYPE_PARAM_CHANGE(32,"参数变更"),
    TYPE_PASSWORD_CHANGE(33,"密码修改"),
    TYPE_LOGIN_TIMEOUT(34,"登录超时");
    private Integer type;
src/main/java/com/whyc/exception/CustomExceptionResultHandler.java
@@ -1,6 +1,9 @@
package com.whyc.exception;
import com.whyc.constant.UserOperation;
import com.whyc.dto.Response;
import com.whyc.util.ActionUtil;
import com.whyc.util.CommonUtil;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
@@ -9,6 +12,7 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
@@ -20,8 +24,15 @@
    /**错误捕捉,状态码:202*/
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.ACCEPTED)
    public Response sendErrorResponse2Defined(Exception e, HttpServletResponse response){
        return new Response().setII(0,"接口请求异常,请联系软件人员进行处理.异常信息"+e.toString());
    public Response sendErrorResponse2Defined(Exception e, HttpServletResponse response, HttpServletRequest request){
        String exceptionStr = e.toString();
        //单项提取-登录超时
        if(exceptionStr.contains("login") && exceptionStr.contains("imeout")){
            CommonUtil.record(ActionUtil.getUser().getUId(), UserOperation.TYPE_LOGIN_TIMEOUT.getType(),"登录请求超时","异常信息:"+ exceptionStr);
        }else {
            CommonUtil.record(ActionUtil.getUser().getUId(), UserOperation.TYPE_EXCEPTION.getType(), "接口请求异常", "异常信息:" + exceptionStr);
        }
        return new Response().setII(0,"接口请求异常,请联系软件人员进行处理.异常信息"+ exceptionStr);
    }
src/main/java/com/whyc/service/LoginService.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.whyc.constant.UserConstant;
import com.whyc.constant.UserOperation;
import com.whyc.constant.YamlProperties;
import com.whyc.dto.Response;
import com.whyc.mapper.PageParamMapper;
@@ -12,6 +13,7 @@
import com.whyc.pojo.UserClient;
import com.whyc.pojo.UserInf;
import com.whyc.util.ActionUtil;
import com.whyc.util.CommonUtil;
import com.whyc.util.RSAUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
@@ -101,60 +103,92 @@
        Response<Object> response = new Response<>();
        deliveredCode = deliveredCode.toUpperCase();
        String fontDynamicCode = (String) ActionUtil.getSession().getAttribute("fontDynamicCode");
        if (fontDynamicCode==null||"".equals(fontDynamicCode)){
            return response.set(1,false,"请刷新验证码");
        if (fontDynamicCode == null || "".equals(fontDynamicCode)) {
            return response.set(1, false, "请刷新验证码");
        }
        if (!deliveredCode.equals(fontDynamicCode.toUpperCase())){
            return response.set(1,false,"验证码错误");
        if (!deliveredCode.equals(fontDynamicCode.toUpperCase())) {
            return response.set(1, false, "验证码错误");
        }
        //验证正确,清除验证码
        ActionUtil.getSession().removeAttribute("fontDynamicCode");
        String password = "";
        try {
            password = URLDecoder.decode(pwd, "utf-8");
        }catch (UnsupportedEncodingException e){
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String[] dataArr = RSAUtil.decryptFront(password, RSAUtil.fontSeparator);
        //验签md5
        if(!dataArr[1].equals(ActionUtil.EncryptionMD5(org.apache.commons.lang3.StringUtils.trim(dataArr[0])).toString())){
            return response.set(1,false,"密码验签失败");
        if (!dataArr[1].equals(ActionUtil.EncryptionMD5(org.apache.commons.lang3.StringUtils.trim(dataArr[0])).toString())) {
            return response.set(1, false, "密码验签失败");
        }
        UsernamePasswordToken userToken = new UsernamePasswordToken(userName, dataArr[0]);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(userToken);
        }catch (Exception e){
            String message = e.getMessage();
            if(message.contains("did not match the expected credentials")){
                return response.set(1,false,"密码错误");
            }
            return response.set(1,false,message);
        }
        ServletContext servletContext = request.getServletContext();
        Enumeration<String> attributeNames = servletContext.getAttributeNames();
        try {
            subject.login(userToken);
        } catch (Exception e) {
            String message = e.getMessage();
            if (message.contains("did not match the expected credentials")) {
                //密码错误,记录次数
                //内存中查找该用户中的登录失败次数
                int loginFailTimes = 0;
                List<String> loginFailAttributeList = new LinkedList<>();
                while (attributeNames.hasMoreElements()){
                    String attributeName = attributeNames.nextElement();
                    if(attributeName.contains(userName+"_login_fail_times_")){
                        loginFailTimes++;
                        loginFailAttributeList.add(attributeName);
                    }
                }
                //查询账号密码错误限制次数
                PageParam loginFailTimesLimit = pageParamMapper.findByCategoryId(9).get(0);
                if((++loginFailTimes)==loginFailTimesLimit.getStatus()){
                    //达到限制次数,锁定账号
                    //userService.lock(subject.getUId());
                    //清除登录错误次数统计
                    loginFailAttributeList.forEach(servletContext::removeAttribute);
                }else {
                    servletContext.setAttribute(userName + "_login_fail_times_"+System.currentTimeMillis(), 0);
                }
                CommonUtil.record(0, UserOperation.TYPE_LOGIN_FAIL.getType(), UserOperation.TYPE_LOGIN_FAIL.getTypeName());
                return response.set(1, false, "密码错误");
            }
            return response.set(1, false, message);
        }
        QueryWrapper<UserInf> queryWrapper = Wrappers.query();
        queryWrapper.select("uId","status","visit_ip","visit_time","password_update_time","last_login_time").eq("uName",userName);
        queryWrapper.select("uId", "status", "visit_ip", "visit_time", "password_update_time", "last_login_time").eq("uName", userName);
        UserInf userInf = userMapper.selectOne(queryWrapper);
        if (subject.isAuthenticated()){
        if (subject.isAuthenticated()) {
            //每个登录的用户都有一个全局变量,里面存着对应的SessionId;
            //同一个账号,后面登录的,会挤掉之前登录的SessionId,这个todo,做限制账号同时登陆人数为1
            //查询账号状态
            if(userInf.getStatus()!=1){
                switch (userInf.getStatus()){
                    case 0: response.setMsg("当前账号的状态异常,无法登录. 异常信息为: "+ UserConstant.ACCOUNT_STATUS_CANCEL.getLabel());break;
                    case 2: response.setMsg("当前账号的状态异常,无法登录. 异常信息为: "+ UserConstant.ACCOUNT_STATUS_HIBERNATE.getLabel());break;
                    case 3: response.setMsg("当前账号的状态异常,无法登录. 异常信息为: "+ UserConstant.ACCOUNT_STATUS_LOCK.getLabel());break;
                    case 4: response.setMsg("当前账号的状态异常,无法登录. 异常信息为: "+ UserConstant.ACCOUNT_STATUS_LOCK_FAIL.getLabel());break;
                    default:response.setMsg("当前账号的状态异常,无法登录. 异常信息为: 无");
            if (userInf.getStatus() != 1) {
                switch (userInf.getStatus()) {
                    case 0:
                        response.setMsg("当前账号的状态异常,无法登录. 异常信息为: " + UserConstant.ACCOUNT_STATUS_CANCEL.getLabel());
                        break;
                    case 2:
                        response.setMsg("当前账号的状态异常,无法登录. 异常信息为: " + UserConstant.ACCOUNT_STATUS_HIBERNATE.getLabel());
                        break;
                    case 3:
                        response.setMsg("当前账号的状态异常,无法登录. 异常信息为: " + UserConstant.ACCOUNT_STATUS_LOCK.getLabel());
                        break;
                    case 4:
                        response.setMsg("当前账号的状态异常,无法登录. 异常信息为: " + UserConstant.ACCOUNT_STATUS_LOCK_FAIL.getLabel());
                        break;
                    default:
                        response.setMsg("当前账号的状态异常,无法登录. 异常信息为: 无");
                }
                return  response.set(1,false);
                return response.set(1, false);
            }
            //严格标准下的规则校验
            if(YamlProperties.systemType == 2){
            if (YamlProperties.systemType == 2) {
                //登录之前,首先校验允许时间和登录ip
                boolean ipPass = true;
@@ -166,48 +200,47 @@
                ipRules = Arrays.asList(ipRuleStr.split(","));
                Calendar instance = Calendar.getInstance();
                String hourOfDay = String.format("%1$02d",instance.get(Calendar.HOUR_OF_DAY));
                String hourOfDay = String.format("%1$02d", instance.get(Calendar.HOUR_OF_DAY));
                int minute = instance.get(Calendar.MINUTE);
                int second = instance.get(Calendar.SECOND);
                String nowTime = hourOfDay+":"+minute+":"+second;
                String nowTime = hourOfDay + ":" + minute + ":" + second;
                //登录时间校验
                if(nowTime.compareTo(firstTime)>=0 && nowTime.compareTo(lastTime)<=0){
                if (nowTime.compareTo(firstTime) >= 0 && nowTime.compareTo(lastTime) <= 0) {
                    //登录ip校验
                    String clientIp = ActionUtil.getRequest().getRemoteAddr();
                    if(!ipRules.contains("*")){
                        for(String ipRule:ipRules){
                    if (!ipRules.contains("*")) {
                        for (String ipRule : ipRules) {
                            ipPass = true;
                            //ip规则格式为 * 或者 xxx.xxx.x.x
                            String[] ipArr = clientIp.split("\\.");
                            String[] ipRuleArr = ipRule.split("\\.");
                            for (int i = 0; i < ipRuleArr.length; i++) {
                                if(!ipRuleArr[i].equals("*") && !ipRuleArr[i].equals(ipArr[i])){
                                if (!ipRuleArr[i].equals("*") && !ipRuleArr[i].equals(ipArr[i])) {
                                    ipPass = false;
                                    break;
                                }
                            }
                            if(ipPass){
                            if (ipPass) {
                                break;
                            }
                        }
                    }
                    if(!ipPass){
                        return response.set(1,false,"您的IP禁止访问,请知晓");
                    if (!ipPass) {
                        return response.set(1, false, "您的IP禁止访问,请知晓");
                    }
                }else{
                    return response.set(1,false,"登录时间不在允许的时间范围内");
                } else {
                    return response.set(1, false, "登录时间不在允许的时间范围内");
                }
                //首次登录,密码修改;超过3个月未修改密码,强制修改密码
                Date passwordUpdateTime = userInf.getPasswordUpdateTime();
                Calendar now = Calendar.getInstance();
                now.add(Calendar.MONTH,-3);
                if(passwordUpdateTime==null){ //密码修改时间为空,尚未修改初始口令
                now.add(Calendar.MONTH, -3);
                if (passwordUpdateTime == null) { //密码修改时间为空,尚未修改初始口令
                    response.setCode(3);
                    response.setData(false);
                    response.setMsg("首次登录,请先修改初始化口令");
                    return response;
                }
                else if(passwordUpdateTime.compareTo(now.getTime()) < 0){
                } else if (passwordUpdateTime.compareTo(now.getTime()) < 0) {
                    response.setCode(2);
                    response.setData(false);
                    response.setMsg("超过3个月没有修改口令,请修改口令后重新登录");
@@ -216,14 +249,14 @@
            }
            //登录成功
            servletContext.setAttribute(userName,request.getSession().getId());
            servletContext.setAttribute(userName, request.getSession().getId());
            //Session存储当前用户及权限组列表
            request.getSession().setAttribute("user",subject.getPrincipal());
            request.getSession().setAttribute("permits",ActionUtil.getGson().toJson(permitGroupUserService.getItemList(userInf.getUId())));
            request.getSession().setAttribute("user", subject.getPrincipal());
            request.getSession().setAttribute("permits", ActionUtil.getGson().toJson(permitGroupUserService.getItemList(userInf.getUId())));
            //清除账号登录失败记录
            while (attributeNames.hasMoreElements()){
            while (attributeNames.hasMoreElements()) {
                String attributeName = attributeNames.nextElement();
                if(attributeName.contains(userName +"_login_fail_times_")){
                if (attributeName.contains(userName + "_login_fail_times_")) {
                    servletContext.removeAttribute(attributeName);
                }
            }
@@ -234,31 +267,10 @@
            dataList.add(subject.getPrincipal());
            int permitGroupId = permitGroupUserService.getPermitGroupId(userInf.getUId());
            dataList.add(permitGroupId);
            return new Response<>().setII(1,true,dataList,"登录成功");
            CommonUtil.record(((UserInf) subject.getPrincipal()).getUId(), UserOperation.TYPE_LOGIN.getType(), UserOperation.TYPE_LOGIN.getTypeName());
            return new Response<>().setII(1, true, dataList, "登录成功");
        }
        //密码错误,记录次数
        //内存中查找该用户中的登录失败次数
        int loginFailTimes = 0;
        List<String> loginFailAttributeList = new LinkedList<>();
        while (attributeNames.hasMoreElements()){
            String attributeName = attributeNames.nextElement();
            if(attributeName.contains(userName+"_login_fail_times_")){
                loginFailTimes++;
                loginFailAttributeList.add(attributeName);
            }
        }
        //查询账号密码错误限制次数
        PageParam loginFailTimesLimit = pageParamMapper.findByCategoryId(9).get(0);
        if((++loginFailTimes)==loginFailTimesLimit.getStatus()){
            //达到限制次数,锁定账号
            userService.lock(userInf.getUId());
            //清除登录错误次数统计
            loginFailAttributeList.forEach(servletContext::removeAttribute);
        }else {
            servletContext.setAttribute(userName + "_login_fail_times_"+System.currentTimeMillis(), 0);
        }
        return new Response<>().set(1,false,"密码错误");
        return new Response().set(1,false,"认证未通过");
    }
    public Response loginWithUKey(String userName, String password, HttpServletRequest request) {
src/main/java/com/whyc/service/UserLogService.java
@@ -1,12 +1,14 @@
package com.whyc.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.whyc.constant.UserOperation;
import com.whyc.mapper.UserLogMapper;
import com.whyc.pojo.UserLog;
import com.whyc.util.ActionUtil;
import com.whyc.util.DateUtil;
import com.whyc.util.ExcelUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
@@ -50,4 +52,27 @@
        String now = DateUtil.YYYY_MM_DD_HH_MM_SS_UNION.format(new Date());
        ExcelUtil.exportExcel("UserLog-"+now,"测试sheet1",columnTitleArr,value,new HSSFWorkbook(),response);
    }
    public void record(long uId, int operationType, String msg) {
        UserLog userLog = new UserLog();
        userLog.setTerminalIp(ActionUtil.getRequest().getRemoteAddr());
        //userLog.setTerminalIp(request.getRemoteAddr());
        userLog.setOperationTime(new Date());
        userLog.setUId((int)(uId));
        userLog.setOperationType(operationType);
        userLog.setOperationMsg(msg);
        mapper.insert(userLog);
    }
    public void record(long uId, int operationType, String msg,String msgDetail) {
        UserLog userLog = new UserLog();
        userLog.setTerminalIp(ActionUtil.getRequest().getRemoteAddr());
        //userLog.setTerminalIp(request.getRemoteAddr());
        userLog.setOperationTime(new Date());
        userLog.setUId((int)(uId));
        userLog.setOperationType(operationType);
        userLog.setOperationMsg(msg);
        userLog.setOperationDetail(msgDetail);
        mapper.insert(userLog);
    }
}
src/main/java/com/whyc/util/ActionUtil.java
@@ -21,6 +21,9 @@
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Optional;
import static jdk.nashorn.internal.objects.NativeNumber.toFixed;
public class ActionUtil{
@@ -183,8 +186,8 @@
        a.roll(Calendar.DATE, -1);  
        int maxDate = a.get(Calendar.DATE);  
        return maxDate;  
    }
    }
    /**
     * 
     * @return 获取当前session 中的用户对象
@@ -194,9 +197,9 @@
        Object obj=session.getAttribute("user");
        UserInf userInf = new UserInf();
        if(obj==null){
            userInf.setUName("superuser");
            userInf.setUId(1002L);
            userInf.setURole(1);
            userInf.setUName("未登录的用户账号");
            userInf.setUId(0L);
            userInf.setURole(0);
        }else{
            userInf=(UserInf) session.getAttribute("user");
        }
src/main/java/com/whyc/util/CommonUtil.java
@@ -1,15 +1,27 @@
package com.whyc.util;
import com.whyc.pojo.UserInf;
import com.whyc.service.UserLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
/**
 * 通用工具列
 */
@Component
public class CommonUtil {
    private static UserLogService userLogService;
    @Autowired
    public void setUserLogService(UserLogService userLogService) {
        CommonUtil.userLogService = userLogService;
    }
    /**获取当前Session中的属性user*/
    public static UserInf getUser(HttpServletRequest request) {
@@ -21,4 +33,14 @@
        File jarFile = applicationHome.getDir();
        return jarFile.toString();
    }
    /**手动记录特定日志*/
    public static void record(long uId, int operationType, String msg){
        userLogService.record(uId,operationType,msg);
    }
    /**手动记录特定日志*/
    public static void record(long uId, int operationType, String msg,String msgDetail){
        userLogService.record(uId,operationType,msg,msgDetail);
    }
}