Browse Source

starter-api增加接口切面与请求详情日志记录

Signed-off-by: luoshi <luoshi@qmth.com.cn>
luoshi 2 years ago
parent
commit
74dac2bdd5
25 changed files with 374 additions and 67 deletions
  1. 1 1
      core-logging/src/main/java/com/qmth/boot/core/logger/context/LoggerContextService.java
  2. 20 0
      core-security/src/main/java/com/qmth/boot/core/security/annotation/Permission.java
  3. 0 9
      core-security/src/main/java/com/qmth/boot/core/security/service/AuthorizationService.java
  4. 3 3
      core-security/src/main/java/com/qmth/boot/core/security/service/impl/BaseAuthorizationSupport.java
  5. 0 4
      core-security/src/test/java/com/qmth/boot/test/core/security/AuthTest.java
  6. 0 6
      core-security/src/test/java/com/qmth/boot/test/core/security/model/TestAuthorizationService.java
  7. 3 3
      core-solar/src/main/java/com/qmth/boot/core/solar/api/SolarApiClient.java
  8. 3 1
      core-solar/src/main/java/com/qmth/boot/core/solar/model/AppControl.java
  9. 5 1
      core-solar/src/main/java/com/qmth/boot/core/solar/model/AppInfo.java
  10. 1 1
      core-solar/src/main/java/com/qmth/boot/core/solar/model/AppLicense.java
  11. 3 3
      core-solar/src/main/java/com/qmth/boot/core/solar/service/SolarService.java
  12. 18 0
      core-solar/src/test/java/com/qmth/boot/test/solar/AppLicenseUtilTest.java
  13. 4 0
      starter-api/pom.xml
  14. 9 0
      starter-api/src/main/java/com/qmth/boot/api/annotation/Aac.java
  15. 1 1
      starter-api/src/main/java/com/qmth/boot/api/config/ApiAutoConfiguration.java
  16. 119 0
      starter-api/src/main/java/com/qmth/boot/api/config/ApiConfigService.java
  17. 58 0
      starter-api/src/main/java/com/qmth/boot/api/config/ApiControllerAspect.java
  18. 9 0
      starter-api/src/main/java/com/qmth/boot/api/config/ApiProperties.java
  19. 9 16
      starter-api/src/main/java/com/qmth/boot/api/interceptor/AbstractInterceptor.java
  20. 5 1
      starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/AuthorizationInterceptor.java
  21. 8 15
      starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/MetricsInterceptor.java
  22. 5 1
      starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/RateLimitInterceptor.java
  23. 5 1
      starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/ValveInterceptor.java
  24. 74 0
      starter-api/src/main/java/com/qmth/boot/api/utils/RequestTrace.java
  25. 11 0
      starter-api/src/main/java/com/qmth/boot/api/utils/RequestUtil.java

+ 1 - 1
core-logging/src/main/java/com/qmth/boot/core/logger/context/LoggerContextService.java

@@ -76,7 +76,7 @@ public class LoggerContextService {
         while (iterator.hasNext()) {
             Appender<ILoggingEvent> appender = iterator.next();
             if (appender instanceof ConsoleAppender) {
-                ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<>();
+                ConsoleAppender<ILoggingEvent> consoleAppender = (ConsoleAppender<ILoggingEvent>) appender;
                 consoleAppender.setEncoder(encoder);
                 consoleAppender.start();
                 hasConsole = true;

+ 20 - 0
core-security/src/main/java/com/qmth/boot/core/security/annotation/Permission.java

@@ -0,0 +1,20 @@
+package com.qmth.boot.core.security.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义权限配置
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface Permission {
+
+    /**
+     * 允许的数据范围
+     *
+     * @return
+     */
+    Class clazz();
+
+}

+ 0 - 9
core-security/src/main/java/com/qmth/boot/core/security/service/AuthorizationService.java

@@ -18,13 +18,4 @@ public interface AuthorizationService<T extends AccessEntity> {
      */
     T findByIdentity(String identity, SignatureType type, String path);
 
-    /**
-     * 根据鉴权对象判断是否有路径访问权限
-     *
-     * @param accessEntity
-     * @param path
-     * @return
-     */
-    boolean hasPermission(T accessEntity, String path);
-
 }

+ 3 - 3
core-security/src/main/java/com/qmth/boot/core/security/service/impl/BaseAuthorizationSupport.java

@@ -68,9 +68,9 @@ public class BaseAuthorizationSupport implements AuthorizationSupport {
                     AuthorizationException.TOKEN_ERROR;
         }
         // 提交请求对象和接口地址进行权限验证
-        if (!service.hasPermission(ae, uri)) {
-            throw AuthorizationException.NO_PERMISSION;
-        }
+        //if (!service.hasPermission(ae, uri)) {
+        //    throw AuthorizationException.NO_PERMISSION;
+        //}
         return ae;
     }
 

+ 0 - 4
core-security/src/test/java/com/qmth/boot/test/core/security/AuthTest.java

@@ -62,13 +62,11 @@ public class AuthTest {
             Assert.assertEquals(ee, AuthorizationException.SECRET_ERROR);
         }
         authorization.secret = accessSecret;
-        authorization.permission = false;
         try {
             support.validateSignature(signature, method, secretPath, String.valueOf(time));
         } catch (AuthorizationException ee) {
             Assert.assertEquals(ee, AuthorizationException.NO_PERMISSION);
         }
-        authorization.permission = true;
         AccessEntity entity = support.validateSignature(signature, method, secretPath, String.valueOf(time));
         Assert.assertNotNull(entity);
         Assert.assertEquals(entity.getIdentity(), accessKey);
@@ -129,13 +127,11 @@ public class AuthTest {
             Assert.assertEquals(ee, AuthorizationException.TOKEN_ERROR);
         }
         authorization.secret = token;
-        authorization.permission = false;
         try {
             support.validateSignature(signature, method, tokenPath, String.valueOf(time));
         } catch (AuthorizationException ee) {
             Assert.assertEquals(ee, AuthorizationException.NO_PERMISSION);
         }
-        authorization.permission = true;
         AccessEntity entity = support.validateSignature(signature, method, tokenPath, String.valueOf(time));
         Assert.assertNotNull(entity);
         Assert.assertEquals(entity.getIdentity(), sessionId);

+ 0 - 6
core-security/src/test/java/com/qmth/boot/test/core/security/model/TestAuthorizationService.java

@@ -7,8 +7,6 @@ public class TestAuthorizationService implements AuthorizationService<TestAccess
 
     public String secret;
 
-    public boolean permission = true;
-
     public boolean disturb = false;
 
     @Override
@@ -22,8 +20,4 @@ public class TestAuthorizationService implements AuthorizationService<TestAccess
         return entity;
     }
 
-    @Override
-    public boolean hasPermission(TestAccessEntity accessEntity, String path) {
-        return permission;
-    }
 }

+ 3 - 3
core-solar/src/main/java/com/qmth/boot/core/solar/api/SolarApiClient.java

@@ -15,15 +15,15 @@ import java.util.List;
 public interface SolarApiClient {
 
     /**
-     * 获取当前访问应用信息接口
+     * 获取当前访问应用部署信息接口
      *
      * @return
      */
-    @POST("app/info")
+    @POST("app/deploy/info")
     AppInfo getAppInfo();
 
     /**
-     * 获取当前应用可访问机构列表
+     * 获取当前应用部署可访问机构列表
      *
      * @param pageNumber
      * @param pageSize

+ 3 - 1
core-solar/src/main/java/com/qmth/boot/core/solar/model/AppControl.java

@@ -1,12 +1,14 @@
 package com.qmth.boot.core.solar.model;
 
 /**
- * 应用控制参数
+ * 应用部署控制参数
  */
 public class AppControl {
 
+    //过期时间戳
     private Long expireTime;
 
+    //最大在线人数
     private Integer maxOnlineUserCount;
 
     /**

+ 5 - 1
core-solar/src/main/java/com/qmth/boot/core/solar/model/AppInfo.java

@@ -1,16 +1,20 @@
 package com.qmth.boot.core.solar.model;
 
 /**
- * 应用基础信息
+ * 应用部署信息
  */
 public class AppInfo {
 
+    //内部主键
     private Long id;
 
+    //部署名称
     private String name;
 
+    //应用编码
     private String code;
 
+    //控制参数
     private AppControl control;
 
     public AppInfo() {

+ 1 - 1
core-solar/src/main/java/com/qmth/boot/core/solar/model/AppLicense.java

@@ -4,7 +4,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * 由许可文件激活的应用信息,比应用基础信息多了机构集合
+ * 由授权文件激活的应用部署信息,额外附带了允许访问的机构信息
  */
 public class AppLicense extends AppInfo {
 

+ 3 - 3
core-solar/src/main/java/com/qmth/boot/core/solar/service/SolarService.java

@@ -32,7 +32,7 @@ public class SolarService {
     }
 
     /**
-     * 获取APP基本信息
+     * 获取应用部署信息
      *
      * @return
      */
@@ -41,7 +41,7 @@ public class SolarService {
     }
 
     /**
-     * 获取APP控制参数
+     * 获取应用控制参数
      *
      * @return
      */
@@ -50,7 +50,7 @@ public class SolarService {
     }
 
     /**
-     * 获取APP可以访问的机构集合,自动判断在线/离线激活两种模式
+     * 获取当前应用可以访问的机构集合,自动判断在线/离线激活两种模式
      *
      * @return
      */

+ 18 - 0
core-solar/src/test/java/com/qmth/boot/test/solar/AppLicenseUtilTest.java

@@ -0,0 +1,18 @@
+package com.qmth.boot.test.solar;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.qmth.boot.tools.crypto.RSA;
+import com.qmth.boot.tools.device.DeviceInfo;
+import com.qmth.boot.tools.models.ByteArray;
+
+import java.io.File;
+
+public class AppLicenseUtilTest {
+
+    //@Test
+    public void testDeviceInfo() throws Exception {
+        RSA.getPublicKey(ByteArray.fromFile(new File("/Users/luoshi/Downloads/ops/rsa/test/test-public.key")).value())
+                .encrypt(new ObjectMapper().writeValueAsBytes(DeviceInfo.current()))
+                .toFile(new File("/Users/luoshi/Downloads/solar/device-test.info"));
+    }
+}

+ 4 - 0
starter-api/pom.xml

@@ -16,6 +16,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.qmth.boot</groupId>
             <artifactId>tools-signature</artifactId>

+ 9 - 0
starter-api/src/main/java/com/qmth/boot/api/annotation/Aac.java

@@ -1,8 +1,10 @@
 package com.qmth.boot.api.annotation;
 
 import com.qmth.boot.core.enums.Platform;
+import com.qmth.boot.core.security.annotation.Permission;
 import com.qmth.boot.tools.signature.SignatureType;
 
+import javax.validation.constraints.Null;
 import java.lang.annotation.*;
 
 /**
@@ -69,4 +71,11 @@ public @interface Aac {
      */
     BOOL metrics() default BOOL.NULL;
 
+    /**
+     * 接口定义需要的权限
+     *
+     * @return
+     */
+    Permission permit() default @Permission(clazz = Null.class);
+
 }

+ 1 - 1
starter-api/src/main/java/com/qmth/boot/api/config/ApiAutoConfiguration.java

@@ -4,7 +4,7 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
-@ComponentScan("com.qmth.boot.api.*")
+@ComponentScan("com.qmth.boot.api")
 public class ApiAutoConfiguration {
 
 }

+ 119 - 0
starter-api/src/main/java/com/qmth/boot/api/config/ApiConfigService.java

@@ -0,0 +1,119 @@
+package com.qmth.boot.api.config;
+
+import com.qmth.boot.api.annotation.Aac;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.validation.constraints.Null;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class ApiConfigService implements ApplicationListener<ContextRefreshedEvent> {
+
+    protected static final Logger log = LoggerFactory.getLogger(ApiConfigService.class);
+
+    private ApiProperties apiProperties;
+
+    private Map<Method, AacConfig> configMap;
+
+    private Map<String, Object> uriPermissionMap;
+
+    public ApiConfigService(ApiProperties apiProperties) {
+        this.apiProperties = apiProperties;
+        this.configMap = new HashMap<>();
+        this.uriPermissionMap = new HashMap<>();
+    }
+
+    public AacConfig getAacConfig(HandlerMethod handlerMethod) {
+        return configMap.get(handlerMethod.getMethod());
+    }
+
+    @Override
+    public synchronized void onApplicationEvent(ContextRefreshedEvent event) {
+        if (event.getApplicationContext().getParent() == null) {
+            Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(RestController.class);
+            for (Object bean : beans.values()) {
+                init(bean);
+            }
+            beans = event.getApplicationContext().getBeansWithAnnotation(Controller.class);
+            for (Object bean : beans.values()) {
+                init(bean);
+            }
+            log.info("ApiConfigService inited, aac method count={}, uri permission count={}", configMap.size(),
+                    uriPermissionMap.size());
+        }
+    }
+
+    private void init(Object bean) {
+        Class<?> clazz = AopUtils.getTargetClass(bean);
+        String[] parentUri = null;
+        RequestMapping parentMapping = AnnotationUtils.findAnnotation(clazz, RequestMapping.class);
+        if (parentMapping != null) {
+            parentUri = parentMapping.value();
+        }
+        if (parentUri == null || parentUri.length == 0) {
+            parentUri = new String[] { "" };
+        }
+        Method[] methods = clazz.getMethods();
+        for (Method method : methods) {
+            String[] uri = new String[] { "" };
+            RequestMapping rm = AnnotationUtils.findAnnotation(method, RequestMapping.class);
+            PostMapping post = AnnotationUtils.findAnnotation(method, PostMapping.class);
+            GetMapping get = AnnotationUtils.findAnnotation(method, GetMapping.class);
+            PutMapping put = AnnotationUtils.findAnnotation(method, PutMapping.class);
+            DeleteMapping delete = AnnotationUtils.findAnnotation(method, DeleteMapping.class);
+            if (rm != null) {
+                uri = rm.value();
+            } else if (post != null) {
+                uri = post.value();
+            } else if (get != null) {
+                uri = get.value();
+            } else if (put != null) {
+                uri = put.value();
+            } else if (delete != null) {
+                uri = delete.value();
+            } else {
+                continue;
+            }
+            initMethod(clazz, method, parentUri, uri);
+        }
+    }
+
+    private void initMethod(Class<?> clazz, Method method, String[] prefixs, String[] uris) {
+        AacConfig ac = new AacConfig(apiProperties);
+        ac.merge(new AacConfig(AnnotationUtils.findAnnotation(clazz, Aac.class)));
+        ac.merge(new AacConfig(AnnotationUtils.findAnnotation(method, Aac.class)));
+        configMap.put(method, ac);
+
+        Aac aac = AnnotationUtils.findAnnotation(method, Aac.class);
+        if (aac != null && !aac.permit().clazz().equals(Null.class)) {
+            List<String> uriList = concat(prefixs, uris);
+            for (String uri : uriList) {
+                uriPermissionMap.put(uri, aac.permit());
+            }
+        }
+        log.debug("Api method inited, {}:{}", clazz.getName(), method.getName());
+    }
+
+    private List<String> concat(String[] prefixs, String[] uris) {
+        List<String> result = new LinkedList<>();
+        for (String prefix : prefixs) {
+            for (String uri : uris) {
+                result.add(prefix.concat(uri));
+            }
+        }
+        return result;
+    }
+}

+ 58 - 0
starter-api/src/main/java/com/qmth/boot/api/config/ApiControllerAspect.java

@@ -0,0 +1,58 @@
+package com.qmth.boot.api.config;
+
+import com.qmth.boot.api.utils.RequestTrace;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Component
+@Aspect
+public class ApiControllerAspect {
+
+    private ApiProperties apiProperties;
+
+    /**
+     * 构造函数
+     */
+    public ApiControllerAspect(ApiProperties apiProperties) {
+        this.apiProperties = apiProperties;
+    }
+
+    /**
+     * 切面注解
+     *
+     * @author
+     */
+    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) "
+            + "|| @annotation(org.springframework.web.bind.annotation.GetMapping) "
+            + "|| @annotation(org.springframework.web.bind.annotation.PostMapping)"
+            + "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)"
+            + "|| @annotation(org.springframework.web.bind.annotation.PutMapping)")
+    public void handlerMethods() {
+
+    }
+
+    /**
+     * 接口处理
+     *
+     * @param joinPoint
+     * @return
+     * @throws Throwable
+     * @author
+     */
+    @Around("handlerMethods()")
+    public Object doAroundWebRequests(ProceedingJoinPoint joinPoint) throws Throwable {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
+                .getRequest();
+        MethodSignature method = (MethodSignature) joinPoint.getSignature();
+        RequestTrace trace = new RequestTrace(apiProperties.isTraceRequest(), request, method, joinPoint.getArgs());
+        return trace.response(joinPoint.proceed());
+    }
+}

+ 9 - 0
starter-api/src/main/java/com/qmth/boot/api/config/ApiProperties.java

@@ -25,6 +25,8 @@ public class ApiProperties implements ApiConstant {
 
     private boolean globalAuth = false;
 
+    private boolean traceRequest = false;
+
     public String getUriPrefix() {
         return uriPrefix;
     }
@@ -65,4 +67,11 @@ public class ApiProperties implements ApiConstant {
         this.globalAuth = globalAuth;
     }
 
+    public boolean isTraceRequest() {
+        return traceRequest;
+    }
+
+    public void setTraceRequest(boolean traceRequest) {
+        this.traceRequest = traceRequest;
+    }
 }

+ 9 - 16
starter-api/src/main/java/com/qmth/boot/api/interceptor/AbstractInterceptor.java

@@ -1,7 +1,7 @@
 package com.qmth.boot.api.interceptor;
 
-import com.qmth.boot.api.annotation.Aac;
 import com.qmth.boot.api.config.AacConfig;
+import com.qmth.boot.api.config.ApiConfigService;
 import com.qmth.boot.api.config.ApiProperties;
 import com.qmth.boot.api.constant.ApiConstant;
 import org.slf4j.Logger;
@@ -10,28 +10,21 @@ import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.HashMap;
-import java.util.Map;
 
 public abstract class AbstractInterceptor extends HandlerInterceptorAdapter implements ApiConstant {
 
     protected final Logger log = LoggerFactory.getLogger(this.getClass());
 
-    private Map<Object, AacConfig> configMap = new HashMap<>();
-
-    protected AacConfig getAacConfig(ApiProperties apiProperties, HttpServletRequest request, Object handler) {
+    protected AacConfig getAacConfig(ApiProperties apiProperties, ApiConfigService apiConfigService,
+            HttpServletRequest request, Object handler) {
         AacConfig aacConfig = getAacConfig(request);
         if (aacConfig == null) {
-            aacConfig = configMap.computeIfAbsent(handler, k -> {
-                AacConfig ac = new AacConfig(apiProperties);
-                if (handler instanceof HandlerMethod) {
-                    HandlerMethod method = (HandlerMethod) handler;
-                    ac.merge(new AacConfig(method.getBeanType().getAnnotation(Aac.class)));
-                    ac.merge(new AacConfig(method.getMethodAnnotation(Aac.class)));
-                }
-                return ac;
-            });
-            request.setAttribute(ATTRIBUTE_AAC_CONFIG, apiProperties);
+            if (handler instanceof HandlerMethod) {
+                aacConfig = apiConfigService.getAacConfig((HandlerMethod) handler);
+            } else {
+                aacConfig = new AacConfig(apiProperties);
+            }
+            request.setAttribute(ATTRIBUTE_AAC_CONFIG, aacConfig);
         }
         return aacConfig;
     }

+ 5 - 1
starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/AuthorizationInterceptor.java

@@ -1,6 +1,7 @@
 package com.qmth.boot.api.interceptor.impl;
 
 import com.qmth.boot.api.config.AacConfig;
+import com.qmth.boot.api.config.ApiConfigService;
 import com.qmth.boot.api.config.ApiProperties;
 import com.qmth.boot.api.exception.DefaultExceptionEnum;
 import com.qmth.boot.api.interceptor.AbstractInterceptor;
@@ -22,6 +23,9 @@ public class AuthorizationInterceptor extends AbstractInterceptor implements Log
     @Resource
     private ApiProperties apiProperties;
 
+    @Resource
+    private ApiConfigService apiConfigService;
+
     @Resource
     private AuthorizationSupport authorizationSupport;
 
@@ -29,7 +33,7 @@ public class AuthorizationInterceptor extends AbstractInterceptor implements Log
     private IPFilterService ipFilterService;
 
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
-        AacConfig config = getAacConfig(apiProperties, request, handler);
+        AacConfig config = getAacConfig(apiProperties, apiConfigService, request, handler);
         if (config.isAuth()) {
             AccessEntity entity;
             try {

+ 8 - 15
starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/MetricsInterceptor.java

@@ -1,9 +1,11 @@
 package com.qmth.boot.api.interceptor.impl;
 
 import com.qmth.boot.api.config.AacConfig;
+import com.qmth.boot.api.config.ApiConfigService;
 import com.qmth.boot.api.config.ApiProperties;
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.boot.api.interceptor.AbstractInterceptor;
+import com.qmth.boot.api.utils.RequestUtil;
 import com.qmth.boot.core.logger.constant.LoggerConstant;
 import com.qmth.boot.core.metrics.service.MetricsService;
 import com.qmth.boot.tools.uuid.FastUUID;
@@ -22,6 +24,9 @@ public class MetricsInterceptor extends AbstractInterceptor implements ApiConsta
     @Resource
     private ApiProperties apiProperties;
 
+    @Resource
+    private ApiConfigService apiConfigService;
+
     @Resource
     private MetricsService metricsService;
 
@@ -42,15 +47,15 @@ public class MetricsInterceptor extends AbstractInterceptor implements ApiConsta
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
             Exception ex) {
-        AacConfig config = getAacConfig(apiProperties, request, handler);
+        AacConfig config = getAacConfig(apiProperties, apiConfigService, request, handler);
         long timecost = getTimecost(request);
         // 接口开启统计且响应不为404时,提交记录
         if (config.isMetrics() && response.getStatus() != HttpStatus.NOT_FOUND.value()) {
             metricsService.record(request.getServletPath(), response.getStatus(), timecost);
         }
         // 记录日志
-        MDC.put(MDC_CALLER, getAttribute(request, ATTRIBUTE_CALLER, "-"));
-        log.info("Api finish: path={}, status={}, timecost={}ms", request.getServletPath(), response.getStatus(),
+        MDC.put(MDC_CALLER, RequestUtil.getAttribute(request, ATTRIBUTE_CALLER, "-"));
+        log.info("{} {} {} {}ms", request.getMethod().toUpperCase(), request.getServletPath(), response.getStatus(),
                 timecost);
         MDC.clear();
     }
@@ -63,16 +68,4 @@ public class MetricsInterceptor extends AbstractInterceptor implements ApiConsta
             return -1;
         }
     }
-
-    private String getAttribute(HttpServletRequest request, String name, String defaultValue) {
-        Object obj = request.getAttribute(name);
-        if (obj instanceof String) {
-            return (String) obj;
-        } else if (defaultValue != null) {
-            return defaultValue;
-        } else {
-            return null;
-        }
-    }
-
 }

+ 5 - 1
starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/RateLimitInterceptor.java

@@ -1,5 +1,6 @@
 package com.qmth.boot.api.interceptor.impl;
 
+import com.qmth.boot.api.config.ApiConfigService;
 import com.qmth.boot.api.config.ApiProperties;
 import com.qmth.boot.api.exception.DefaultExceptionEnum;
 import com.qmth.boot.api.interceptor.AbstractInterceptor;
@@ -22,6 +23,9 @@ public class RateLimitInterceptor extends AbstractInterceptor {
     @Resource
     private ApiProperties apiProperties;
 
+    @Resource
+    private ApiConfigService apiConfigService;
+
     @Resource
     private RateLimitService rateLimitService;
 
@@ -32,7 +36,7 @@ public class RateLimitInterceptor extends AbstractInterceptor {
         String path = request.getServletPath();
         RateLimitRule[] rules = rateLimitPolicy != null ? rateLimitPolicy.getRules(path) : null;
         if (ArrayUtils.isEmpty(rules)) {
-            rules = getAacConfig(apiProperties, request, handler).getRateLimit();
+            rules = getAacConfig(apiProperties, apiConfigService, request, handler).getRateLimit();
         }
         String device = getDeviceIdOrIpAddress(request);
         // 所有限流规则需要全部匹配才通过

+ 5 - 1
starter-api/src/main/java/com/qmth/boot/api/interceptor/impl/ValveInterceptor.java

@@ -1,6 +1,7 @@
 package com.qmth.boot.api.interceptor.impl;
 
 import com.qmth.boot.api.config.AacConfig;
+import com.qmth.boot.api.config.ApiConfigService;
 import com.qmth.boot.api.config.ApiProperties;
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.boot.api.exception.DefaultExceptionEnum;
@@ -23,6 +24,9 @@ public class ValveInterceptor extends AbstractInterceptor implements ApiConstant
     @Resource
     private ApiProperties apiProperties;
 
+    @Resource
+    private ApiConfigService apiConfigService;
+
     @Resource
     private IPFilterService ipFilterService;
 
@@ -34,7 +38,7 @@ public class ValveInterceptor extends AbstractInterceptor implements ApiConstant
     }
 
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
-        AacConfig config = getAacConfig(apiProperties, request, handler);
+        AacConfig config = getAacConfig(apiProperties, apiConfigService, request, handler);
         Platform platform = Platform.findByName(StringUtils.trimToNull(request.getHeader(HEADER_PLATFORM)));
         String deviceId = StringUtils.trimToNull(request.getHeader(HEADER_DEVICE_ID));
         if (config.isStrict()) {

+ 74 - 0
starter-api/src/main/java/com/qmth/boot/api/utils/RequestTrace.java

@@ -0,0 +1,74 @@
+package com.qmth.boot.api.utils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.core.logger.constant.LoggerConstant;
+import com.qmth.boot.core.security.model.AccessEntity;
+import org.apache.commons.lang3.ArrayUtils;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 请求跟踪信息
+ */
+public class RequestTrace {
+
+    private static final Logger log = LoggerFactory.getLogger(RequestTrace.class);
+
+    private boolean enable;
+
+    public RequestTrace(boolean enable, HttpServletRequest request, MethodSignature method, Object[] args) {
+        this.enable = enable && !request.getServletPath().equals(ApiConstant.DEFAULT_ERROR_URI);
+        if (this.enable) {
+            try {
+                Map<String, Object> content = new LinkedHashMap<>();
+                content.put("uri", request.getServletPath());
+                content.put("method", request.getMethod());
+                content.put("content-type", request.getContentType());
+                Map<String, Object> parameter = new HashMap<>();
+                if (ArrayUtils.isNotEmpty(method.getParameterNames())) {
+                    for (int i = 0; i < method.getParameterNames().length; i++) {
+                        filter(parameter, method.getParameterNames()[i], args[i]);
+                    }
+                }
+                content.put("parameter", parameter);
+                // 记录日志
+                MDC.put(LoggerConstant.MDC_CALLER,
+                        RequestUtil.getAttribute(request, ApiConstant.ATTRIBUTE_CALLER, "-"));
+                log.info("Request info: \n{}",
+                        new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(content));
+            } catch (Exception e) {
+                log.error("Trace request error", e);
+            }
+        }
+    }
+
+    private void filter(Map<String, Object> parameter, String name, Object value) {
+        if (value instanceof HttpServletRequest || value instanceof HttpServletResponse || value instanceof AccessEntity
+                || value instanceof MultipartFile) {
+            return;
+        }
+        parameter.put(name, value);
+    }
+
+    public Object response(Object result) {
+        if (this.enable) {
+            try {
+                log.info("Response body: \n{}", new ObjectMapper().writerWithDefaultPrettyPrinter()
+                        .writeValueAsString(result != null ? result : "null"));
+            } catch (Exception e) {
+                log.error("Trace response error", e);
+            }
+        }
+        return result;
+    }
+}

+ 11 - 0
starter-api/src/main/java/com/qmth/boot/api/utils/RequestUtil.java

@@ -40,4 +40,15 @@ public class RequestUtil implements ApiConstant {
         }
     }
 
+    public static String getAttribute(HttpServletRequest request, String name, String defaultValue) {
+        Object obj = request.getAttribute(name);
+        if (obj instanceof String) {
+            return (String) obj;
+        } else if (defaultValue != null) {
+            return defaultValue;
+        } else {
+            return null;
+        }
+    }
+
 }