spring boot自定义注解拦截接口

该博客介绍了如何使用Spring Boot的AOP(面向切面编程)来实现自定义注解,用于拦截并记录接口请求的审计日志。首先添加了`spring-boot-starter-aop`依赖,然后定义了一个名为`@AuditLog`的注解,包含了请求的模块、方法、登录状态等信息。接着,创建了一个AOP切面,包括`@AfterReturning`和`@AfterThrowing`通知,分别处理正常请求和异常情况,实现了日志的保存。此外,还提供了工具类帮助获取请求信息、处理异常和压缩数据。此功能有助于监控和追踪系统的操作行为。

自定义注解,拦截接口请求

maven依赖管理

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

1 注解编写

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuditLog {

    @AliasFor("title")
    String value() default "" ;

    /**
     * 模块
     */
     String title() default "";

     HttpMethod method() default HttpMethod.GET;



    /**
     * 是否登录用户
     * @return
     */
    boolean isLogin() default true;

    /**
     * 如果非登录用户 传入client作为操作人
     * @return
     */
    String client() default "";

    /**
     * 是否保存请求的参数
     */
     boolean isSaveRequestData() default true;
}

2 编写AOP

2.1 定义pointCut

    @Pointcut("@annotation(com.lcfc.common.log.annotation.AuditLog)")
    public void logPointCut(){}

2.2 定义处理完请求后操作

 @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
    {
        handleLog(joinPoint, null, jsonResult);
    }

2.3 定义异常操作

@AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
    {
        handleLog(joinPoint, e, null);
    }

完成代码

@EnableFeignClients(basePackages = {"com.lcfc"})
@Slf4j
@Component
@Aspect
public class AuditLogAspect {

    @Autowired
    AuditLogService logService;

    @Autowired
    Environment env;

    //是否使用大文件存储
    @Value("${logging.large.store:true}")
    private Boolean largeStore;


    @Pointcut("@annotation(com.lcfc.common.log.annotation.AuditLog)")
    public void logPointCut(){}

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
    {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
    {
        handleLog(joinPoint, e, null);
    }


    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult)
    {
        try
        {
            // 获得注解
            AuditLog controllerLog = AspectUtils.getAnnotation(joinPoint,AuditLog.class);
            if (controllerLog == null)
            {
                return;
            }

            // *========数据库日志=========*//
            AuditLogEntity operLog = new AuditLogEntity();
            operLog.setStatus(Integer.valueOf(Constants.NORMAL));
            // 请求的地址
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            operLog.setOperIp(ip);

            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());

            // 设置请求方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());

            // 是否需要保存request,参数和值
            if (controllerLog.isSaveRequestData())
            {
                // 获取参数的信息,传入到数据库中。
                setRequestValue(joinPoint, operLog);
            }

            saveLog(controllerLog,operLog,joinPoint,e,jsonResult);

        }
        catch (Exception exp)
        {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    public void saveLog(AuditLog log,AuditLogEntity operLog,final JoinPoint joinPoint,final Exception e,Object jsonResult) throws Exception {

        // 设置action动作
        operLog.setBusinessType(log.method().ordinal());
        // 设置模块
        operLog.setTitle(log.title());
        //设置应用名
        String appName = env.getProperty("spring.application.name");
        if (StringUtils.isNotBlank(appName)){
            operLog.setOperApp(appName);
        }

        operLog.setCreateTime(new Date());
        // 返回参数
        String res = JSONObject.toJSONString(jsonResult);
        operLog.setHasCompress(Integer.valueOf(Constants.NORMAL));
        // 字符大于规定 同时不支持大文件存储
        if (res.length()>Constants.STORE_LENGTH.intValue()
                &&!largeStore){
            res = GZIPUtils.compress(res);
            operLog.setHasCompress(Integer.valueOf(Constants.EXCEPTION));
        }
        operLog.setJsonResult(res);

        if (log.isLogin()){
            String username = SecurityUtils.getUsername();
            if (StringUtils.isNotBlank(username))
            {
                operLog.setOperName(username);
                operLog.setCreateBy(username);
            }
        }else if (StringUtils.isNoneBlank(log.client())){
            String username = log.client();
            operLog.setOperName(username);
            operLog.setCreateBy(username);
        }


        if (e != null)
        {
            operLog.setStatus(Integer.valueOf(Constants.EXCEPTION));
            operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
        }
        // 设置方法名称
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        operLog.setMethod(className + "." + methodName + "()");

        ApiOperation operation = AspectUtils.getAnnotation(joinPoint,ApiOperation.class);

        if (operation!=null){
            operLog.setOperDesc(operation.value());
        }

        // 设置操作人类别
//        operLog.setOperatorType(log.operatorType().ordinal());


        // 保存数据库
        logService.saveLog(operLog);
    }



    /**
     * 获取请求的参数,放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, AuditLogEntity operLog) throws Exception
    {
        String params = argsArrayToString(joinPoint.getArgs());
        operLog.setOperParam(StringUtils.substring(params, 0, 2000));
    }


    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (int i = 0; i < paramsArray.length; i++)
            {
                if (paramsArray[i]!=null&&!isFilterObject(paramsArray[i]))
                {
                    try
                    {
                        Object jsonObj = JSONObject.toJSON(paramsArray[i]);
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o)
    {
        Class<?> clazz = o.getClass();
        if (clazz.isArray())
        {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext();)
            {
                return iter.next() instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
            {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }

}

help

Aspect 工具类

/**
 * AOP 工具类
 */
@UtilityClass
public class AspectUtils {

    /**
     * 是否存在注解,如果存在就获取
     * @return
     */
    public  static  <T> T getAnnotation(JoinPoint joinPoint, Class clazz) throws Exception
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null)
        {
            return (T)method.getAnnotation(clazz);
        }
        return null;
    }

}

servlet 工具类

public class ServletUtils {
    /**
     * 获取String参数
     */
    public static String getParameter(String name)
    {
        return getRequest().getParameter(name);
    }

 

    /**
     * 获取request
     */
    public static HttpServletRequest getRequest()
    {
        try
        {
            return getRequestAttributes().getRequest();
        }
        catch (Exception e)
        {
            return null;
        }
    }
    
    
    public static ServletRequestAttributes getRequestAttributes()
    {
        try
        {
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            return (ServletRequestAttributes) attributes;
        }
        catch (Exception e)
        {
            return null;
        }
    }


    /**
     * 获取response
     */
    public static HttpServletResponse getResponse()
    {
        try
        {
            return getRequestAttributes().getResponse();
        }
        catch (Exception e)
        {
            return null;
        }
    }

    /**
     * 获取session
     */
    public static HttpSession getSession()
    {
        return getRequest().getSession();
    }


    public static Map<String, String> getHeaders(HttpServletRequest request)
    {
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        if (enumeration != null)
        {
            while (enumeration.hasMoreElements())
            {
                String key = enumeration.nextElement();
                String value = request.getHeader(key);
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * 将字符串渲染到客户端
     * 
     * @param response 渲染对象
     * @param string 待渲染的字符串
     * @return null
     */
    public static String renderString(HttpServletResponse response, String string)
    {
        try
        {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取Integer参数
     */
    public static Integer getParameterToInt(String name)
    {
        return  Integer.valueOf(getRequest().getParameter(name));
    }



}

ip 工具类

public class IpUtils {

    public static String getIpAddr(HttpServletRequest request)
    {
        String ip = null;

        // X-Forwarded-For:Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
        {
            // Proxy-Client-IP:apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
        {
            // WL-Proxy-Client-IP:weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
        {
            // HTTP_CLIENT_IP:有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
        {
            // X-Real-IP:nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }

        // 有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0)
        {
            ip = ipAddresses.split(",")[0];
        }

        // 还是不能获取到,最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses))
        {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1") ? "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 < 2)
        {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0)
        {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4)
                {
                    return true;
                }
            case SECTION_5:
                switch (b1)
                {
                    case SECTION_6:
                        return true;
                }
            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 > 4294967295L)){
                        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 > 255L)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L)) {
                        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 < 2; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L)) {
                        return null;
                    }
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        }
        catch (NumberFormatException e)
        {
            return null;
        }
        return bytes;
    }

    public static String getHostIp()
    {
        try
        {
            return InetAddress.getLocalHost().getHostAddress();
        }
        catch (UnknownHostException e)
        {
        }
        return "127.0.0.1";
    }

    public static String getHostName()
    {
        try
        {
            return InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException e)
        {
        }
        return "未知";
    }

}

压缩工具类

public class GZIPUtils {
    /**
     * 使用gzip进行压缩
     */
    public static synchronized String compress(String primStr) {
        if (primStr == null || primStr.length() == 0) {
            return primStr;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip = null;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(primStr.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (gzip != null) {
                try {
                    gzip.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return new sun.misc.BASE64Encoder().encode(out.toByteArray());
    }

    /**
     * 使用gzip进行解压缩
     */
    public static synchronized String uncompress(String compressedStr) {
        if (compressedStr == null) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = null;
        GZIPInputStream ginzip = null;
        byte[] compressed = null;
        String decompressed = null;
        try {
            compressed = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr);
            in = new ByteArrayInputStream(compressed);
            ginzip = new GZIPInputStream(in);

            byte[] buffer = new byte[1024];
            int offset = -1;
            while ((offset = ginzip.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            decompressed = out.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ginzip != null) {
                try {
                    ginzip.close();
                } catch (IOException e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
            try {
                out.close();
            } catch (IOException e) {
            }
        }
        return decompressed;
    }

    public static void main(String[] args) {
        
    }
}

spring boot 一定要扫描到AuditLogAspect.java

### 解决Spring Boot自定义注解拦截器失效的问题 在处理Spring Boot中的自定义注解拦截器失效问题时,有几个关键点需要注意。首先,在较新的Spring Boot版本中,`WebMvcConfigurerAdapter`已经被弃用,推荐使用`WebMvcConfigurer`接口来配置MVC组件[^2]。 #### 使用 `WebMvcConfigurer` 为了确保拦截器能够正常工作,应该通过实现`WebMvcConfigurer`接口并重写相应的方法来进行配置: ```java import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { private final MyCustomInterceptor myCustomInterceptor; public WebConfig(MyCustomInterceptor myCustomInterceptor) { this.myCustomInterceptor = myCustomInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myCustomInterceptor).addPathPatterns("/api/**"); } } ``` 上述代码展示了如何注册一个名为`MyCustomInterceptor`的拦截器,并将其应用于路径模式为`/api/**`的所有请求上[^1]。 #### 创建自定义注解及其处理器 对于带有特定注解的目标方法执行前后的操作,可以通过创建自定义注解和相应的AOP切面来实现。下面是一个简单的例子说明这一过程: ##### 定义自定义注解 ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomAnnotation { String value() default ""; } ``` ##### 编写对应的AspectJ切面逻辑 ```java import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.AfterReturning; import org.springframework.stereotype.Component; @Aspect @Component public class CustomAnnotationHandler { @Before("@annotation(com.example.CustomAnnotation)") public void beforeMethodExecution() { System.out.println("Executing method with custom annotation..."); } @AfterReturning("@annotation(com.example.CustomAnnotation)") public void afterMethodExecution() { System.out.println("Finished executing method with custom annotation."); } } ``` 这里假设`com.example.CustomAnnotation`是你之前定义的那个自定义注解的位置。这段代码会在任何被标记了该注解的方法调用前后打印消息到控制台。 #### 验证配置是否正确加载 如果一切设置无误但仍遇到问题,则可能是因为某些地方未按预期加载或初始化。可以尝试启用调试日志级别查看更详细的输出信息,帮助定位潜在错误原因。另外也可以利用单元测试验证各个部分的功能性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

woniyu123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值