springboot AOP 操作日志

该博客介绍了如何在Java项目中实现操作日志管理,包括自定义注解`OperateLog`用于标记需要记录日志的方法,切面`OperationLogAspect`用于拦截并处理这些方法,以及`IpUtils`、`HttpUtils`和`AddressUtils`工具类的使用,用于获取和处理IP地址、HTTP请求和地理地址信息。
摘要由CSDN通过智能技术生成

实体类

@EqualsAndHashCode(callSuper = true)
@Data
@SuppressWarnings("serial")
@ApiModel("OperationLog")
public class OperationLog extends Model<OperationLog> implements Serializable {
    private static final long serialVersionUID = -53946117294772715L;
            
    @ApiModelProperty("id主键")
    @TableId(type = IdType.AUTO)
    private Integer id;
            
    @ApiModelProperty("功能模块")
    private String operModel;
            
    @ApiModelProperty("操作类型")
    private String operType;
            
    @ApiModelProperty("请求参数")
    private Object operParam;
            
    @ApiModelProperty("操作人id")
    private Integer operUserId;
            
    @ApiModelProperty("操作方法")
    private String operMethod;
            
    @ApiModelProperty("操作url")
    private String operUrl;
            
    @ApiModelProperty("操作ip")
    private String operIp;
            
    @ApiModelProperty("操作描述")
    private String operDes;
            
    @ApiModelProperty("操作时间")
    private Date operTime;

    @ApiModelProperty("返回结果")
    private  Object operResult;

    @ApiModelProperty("操作地址")
    private  Object operAddress;
}

自定义注解

/**
 * @Author: xrp
 * @Date: 2021/08/23/9:55
 * @Description
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {
    /** 操作模块**/
    String operateModule() default "";
    /** 操作类型**/
    String operateType() default "";
    /**操作描述 **/
    String operateDes() default "";
}

切面

/**
 * @Author: xrp
 * @Date: 2021/08/23/10:55
 * @Description 切面处理类,日志处理
 */
@Aspect
@Component
public class OperationLogAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(OperationLogAspect.class);

    @Resource
    private OperationLogService operationLogService;

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.xxx.xxx.xxx.OperateLog)")
    public void operationLogPointCut() {
    }

    /**
     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
     *
     * @param joinPoint 切入点
     */

    @AfterReturning(value = "operationLogPointCut()",returning = "jsonResult")
    public void saveOperationLog(JoinPoint joinPoint,Object jsonResult) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        assert requestAttributes != null;
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        OperationLog operationLog = new OperationLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取操作
            OperateLog opLog = method.getAnnotation(OperateLog.class);
            if (opLog != null) {
                String opModule = opLog.operateModule();
                String opType = opLog.operateType();
                String opDesc = opLog.operateDes();
                operationLog.setOperModel(opModule);
                operationLog.setOperType(opType);
                operationLog.setOperDes(opDesc);

            }
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            operationLog.setOperMethod(methodName);
            // 请求的参数
            assert request != null;
            operationLog.setOperParam(JSON.toJSONString(getParameter(method,joinPoint.getArgs())));
            operationLog.setOperUserId(Objects.requireNonNull(UserInfoUtils.getUserInfo()).getUserId());
            operationLog.setOperIp(IpUtils.getIpAddr(request));
            operationLog.setOperUrl(request.getRequestURI());
            operationLog.setOperTime(new Date());
            operationLog.setOperResult(JSON.toJSONString(jsonResult));
            //内网,无法上网环境去除获取地址,否则影响项目相应速度(地址加白名单除外)
            operationLog.setOperAddress(AddressUtils.getRealAddressByIp(IpUtils.getIpAddr(request)));
            operationLogService.save(operationLog);
        } catch (Exception e) {
            LOGGER.info("添加操作日志失败:{}", operationLog);
        }
    }



    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            //将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>(args.length);
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }

工具类

ipUtils

/**
 * @Author: xrp
 * @Date: 2021/08/23/10:22
 * @Description  
 *
 *  public static  final  Integer TWO = 2;
 *  public static  final  Integer FOUR = 4;
 *  public static  final  Long LENGTH =4294967295L;
 *  public static  final  Long LENGTH_1 =255L;
 *  public static  final  Long LENGTH_2 =16777215L;
 *  public static  final  Long LENGTH_3 =65535L;
 *  public static  final  String GBK = "GBK";
 *
 *
 */
public class IpUtils {
    private static final String UNKNOWN_IP_ADDR = "unknown";

    public static String getIpAddr(HttpServletRequest request) {

        if (request == null) {
            return UNKNOWN_IP_ADDR;
        }

        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ip) || UNKNOWN_IP_ADDR.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN_IP_ADDR.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN_IP_ADDR.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN_IP_ADDR.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN_IP_ADDR.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }


    public static boolean internalIp(String ip) {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }

    private static boolean internalIp(byte[] addr) {
        if (addr == null || addr.length < ConstantValue.TWO) {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte section1 = 0x0A; 
        // 172.16.x.x/12
        final byte section2 = (byte) 0xAC;
        final byte section3 = (byte) 0x10;
        final byte section4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte section5 = (byte) 0xC0;
        final byte section6 = (byte) 0xA8;
        switch (b0) {
            case section1:
                return true;
            case section2:
                if (b1 >= section3 && b1 <= section4) {
                    return true;
                }
            case section5:
                return b1 == section6;
            default:
                return false;
        }
    }

    /**
     * 将IPv4地址转换成字节
     *
     * @param text IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(String text) {
        if (text.length() == 0) {
            return null;
        }

        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try {
            long l;
            int i;
            switch (elements.length) {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > ConstantValue.LENGTH)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > ConstantValue.LENGTH_1)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > ConstantValue.LENGTH_2)) {
                        return null;
                    }
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < ConstantValue.TWO; ++i) {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > ConstantValue.LENGTH_1)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > ConstantValue.LENGTH_3)) {
                        return null;
                    }
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < ConstantValue.FOUR; ++i) {
                        l = Integer.parseInt(elements[i]);
                            if ((l < 0L) || (l >ConstantValue.LENGTH_1)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        } catch (NumberFormatException e) {
            return null;
        }
        return bytes;
    }

}

httpUtils

/**
 * @Author: xrp
 * @Date: 2021/08/23/14:29
 * @Description
 */
@Slf4j
public class HttpUtils {

    public static String sendGet(String url, String param, String contentType)
    {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try
        {
            String urlNameString = url + "?" + param;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
            }
            catch (Exception ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }
}

AddressUtills

/**
 * @Author: xrp
 * @Date: 2021/08/23/9:55
 * @Description 获取地址
 */
public class AddressUtils {
    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);

    /**
     * IP地址
     **/
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";

    /**
     * 未知地址
     **/
    public static final String UNKNOWN = "XX XX";

    public static String getRealAddressByIp(String ip) {
        // 内网不查询
        if (IpUtils.internalIp(ip)) {
            return "内网IP";
        }
        try {
            String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", ConstantValue.GBK);
            if (StringUtils.isEmpty(rspStr)) {
                log.error("获取地理位置异常 {}", ip);
                return UNKNOWN;
            }
            JSONObject obj = JSONObject.parseObject(rspStr);
            String region = obj.getString("pro");
            String city = obj.getString("city");
            return String.format("%s %s", region, city);
        } catch (Exception e) {
            log.error("获取地理位置异常 {}", ip);
        }

        return UNKNOWN;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值