diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java index cc301c8f2..b258eda01 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java @@ -6,6 +6,7 @@ import org.hswebframework.web.authorization.ReactiveAuthenticationSupplier; import org.hswebframework.web.authorization.exception.UnAuthorizedException; import org.hswebframework.web.context.ContextKey; import org.hswebframework.web.context.ContextUtils; +import org.hswebframework.web.logger.ReactiveLogger; import org.springframework.beans.factory.annotation.Autowired; import reactor.core.publisher.Mono; @@ -73,9 +74,11 @@ public class UserTokenReactiveAuthenticationSupplier implements ReactiveAuthenti .getByToken(t.getToken()) .filter(UserToken::validate)) .map(tokenMono -> tokenMono - .doOnNext(token->userTokenManager.touch(token.getToken())) + .doOnNext(token -> userTokenManager.touch(token.getToken())) .flatMap(token -> get(thirdPartAuthenticationManager.get(token.getType()), token.getUserId()))) - .orElseGet(Mono::empty)); + .orElseGet(Mono::empty)) + .flatMap(auth -> ReactiveLogger.mdc("userId", auth.getUser().getId()).thenReturn(auth)) + ; } } diff --git a/hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/AuthenticationTests.java b/hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/AuthenticationTests.java index a71759499..7db4be93f 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/AuthenticationTests.java +++ b/hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/AuthenticationTests.java @@ -5,10 +5,12 @@ import org.hswebframework.web.authorization.simple.builder.SimpleAuthenticationB import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory; import org.hswebframework.web.authorization.token.*; import org.hswebframework.web.context.ContextKey; +import org.hswebframework.web.logger.ReactiveLogger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Mono; +import reactor.core.publisher.SignalType; import reactor.test.StepVerifier; import java.util.Collections; @@ -127,12 +129,17 @@ public class AuthenticationTests { return token.getType(); } }; + //获取当前登录用户 Authentication .currentReactive() .map(Authentication::getUser) .map(User::getId) + .doOnEach(ReactiveLogger.on(SignalType.ON_NEXT,(ctx,signal)->{ + System.out.println(ctx); + })) .subscriberContext(acceptContext(ctx -> ctx.put(ContextKey.of(ParsedToken.class), parsedToken))) + // .subscriberContext(ReactiveLogger.start("rid","1")) .as(StepVerifier::create) .expectNext("admin") .verifyComplete(); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DimensionDataAccessHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DimensionDataAccessHandler.java index 3be5c4bcb..2c3638975 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DimensionDataAccessHandler.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DimensionDataAccessHandler.java @@ -114,6 +114,13 @@ public class DimensionDataAccessHandler implements DataAccessHandler { DataAccessHandlerContext context, MappingInfo mappingInfo, Object id) { + + if (id instanceof Param || id instanceof Entity) { + + applyQueryParam(config, context, id); + return id; + } + List dimensions = context.getDimensions(); Set scope = CollectionUtils.isNotEmpty(config.getScope()) ? @@ -138,11 +145,25 @@ public class DimensionDataAccessHandler implements DataAccessHandler { if (id instanceof Publisher) { if (id instanceof Mono) { return ((Mono) id) - .flatMap(r -> reactiveCheck.apply(r instanceof Collection ? ((Collection) r) : Collections.singleton(r))) + .flatMap(r -> { + if (r instanceof Param) { + applyQueryParam(config, context, r); + return Mono.just(r); + } + return reactiveCheck.apply(r instanceof Collection ? ((Collection) r) : Collections.singleton(r)); + + }) .then((Mono) id); } if (id instanceof Flux) { return ((Flux) id) + .filter(v -> { + if (v instanceof Param) { + applyQueryParam(config, context, v); + return false; + } + return true; + }) .collectList() .flatMap(reactiveCheck) .thenMany((Flux) id); @@ -272,7 +293,7 @@ public class DimensionDataAccessHandler implements DataAccessHandler { MappingInfo mappingInfo = getMappingInfo(context).get(cfg.getScopeType()); //根据结果控制 - if (context.getDefinition().getPhased() == Phased.after) { + if (context.getDefinition().getResources().getPhased() == Phased.after) { Object result = context.getParamContext().getInvokeResult(); Set scope = CollectionUtils.isNotEmpty(cfg.getScope()) ? cfg.getScope() : @@ -283,12 +304,14 @@ public class DimensionDataAccessHandler implements DataAccessHandler { String property = mappingInfo.getProperty(); if (result instanceof Mono) { - context.getParamContext().setInvokeResult(((Mono) result). - filter(data -> hasAccessByProperty(scope, property, data))); + context.getParamContext() + .setInvokeResult(((Mono) result). + filter(data -> hasAccessByProperty(scope, property, data))); return true; } else if (result instanceof Flux) { - context.getParamContext().setInvokeResult(((Flux) result). - filter(data -> hasAccessByProperty(scope, property, data))); + context.getParamContext() + .setInvokeResult(((Flux) result). + filter(data -> hasAccessByProperty(scope, property, data))); return true; } return hasAccessByProperty(scope, property, result); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenWebFilter.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenWebFilter.java index b14c5de48..82f8a17a3 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenWebFilter.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenWebFilter.java @@ -5,6 +5,7 @@ import org.hswebframework.web.authorization.events.AuthorizationSuccessEvent; import org.hswebframework.web.authorization.token.ParsedToken; import org.hswebframework.web.authorization.token.UserTokenManager; import org.hswebframework.web.context.ContextUtils; +import org.hswebframework.web.logger.ReactiveLogger; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; @@ -42,7 +43,9 @@ public class UserTokenWebFilter implements WebFilter, BeanPostProcessor { .subscriberContext(ContextUtils.acceptContext(ctx -> Flux.fromIterable(parsers) .flatMap(parser -> parser.parseToken(exchange)) - .subscribe(token -> ctx.put(ParsedToken.class, token)))); + .subscribe(token -> ctx.put(ParsedToken.class, token)))) + .subscriberContext(ReactiveLogger.start("requestId", exchange.getRequest().getId())) + ; } @EventListener diff --git a/hsweb-core/src/main/java/org/hswebframework/web/aop/MethodInterceptorHolder.java b/hsweb-core/src/main/java/org/hswebframework/web/aop/MethodInterceptorHolder.java index 8588d574e..b4e168d0c 100644 --- a/hsweb-core/src/main/java/org/hswebframework/web/aop/MethodInterceptorHolder.java +++ b/hsweb-core/src/main/java/org/hswebframework/web/aop/MethodInterceptorHolder.java @@ -52,14 +52,17 @@ public class MethodInterceptorHolder { String[] argNames = nameDiscoverer.getParameterNames(invocation.getMethod()); Object[] args = invocation.getArguments(); Map argMap = new LinkedHashMap<>(); + String[] names = new String[args.length]; for (int i = 0, len = args.length; i < len; i++) { - argMap.put((argNames == null || argNames[i] == null) ? "arg" + i : argNames[i], args[i]); + names[i] = (argNames == null || argNames.length <= i || argNames[i] == null) ? "arg" + i : argNames[i]; + argMap.put(names[i], args[i]); } return new MethodInterceptorHolder(id, invocation.getMethod(), invocation.getThis(), args, + names, argMap); } @@ -71,6 +74,8 @@ public class MethodInterceptorHolder { private Object[] arguments; + private String[] argumentsNames; + private Map namedArguments; diff --git a/hsweb-core/src/main/java/org/hswebframework/web/logger/ReactiveLogger.java b/hsweb-core/src/main/java/org/hswebframework/web/logger/ReactiveLogger.java new file mode 100644 index 000000000..f8fc64d4e --- /dev/null +++ b/hsweb-core/src/main/java/org/hswebframework/web/logger/ReactiveLogger.java @@ -0,0 +1,112 @@ +package org.hswebframework.web.logger; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Signal; +import reactor.core.publisher.SignalType; +import reactor.util.context.Context; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +@Slf4j +public class ReactiveLogger { + + private static String CONTEXT_KEY = ReactiveLogger.class.getName(); + + public static Function start(String key, String value) { + return start(Collections.singletonMap(key, value)); + } + + public static Mono mdc(String key, String value) { + return Mono.empty() + .subscriberContext(start(key, value)); + } + + public static Function start(Map context) { + return ctx -> { + Optional> maybeContextMap = ctx.getOrEmpty(CONTEXT_KEY); + if (maybeContextMap.isPresent()) { + maybeContextMap.get().putAll(context); + return ctx; + } else { + return ctx.put(CONTEXT_KEY, new LinkedHashMap<>(context)); + } + }; + } + + + public static void log(Context context, Consumer> logger) { + Optional> maybeContextMap = context.getOrEmpty(CONTEXT_KEY); + if (!maybeContextMap.isPresent()) { + logger.accept(new HashMap<>()); + } else { + Map ctx = maybeContextMap.get(); + MDC.setContextMap(ctx); + try { + logger.accept(ctx); + } finally { + MDC.clear(); + } + } + } + + public static Consumer> on(SignalType type, BiConsumer, Signal> logger) { + return signal -> { + if (signal.getType() != type) { + return; + } + Optional> maybeContextMap + = signal.getContext().getOrEmpty(CONTEXT_KEY); + if (!maybeContextMap.isPresent()) { + logger.accept(new HashMap<>(), signal); + } else { + Map ctx = maybeContextMap.get(); + MDC.setContextMap(ctx); + try { + logger.accept(ctx, signal); + } finally { + MDC.clear(); + } + } + }; + } + + public static Mono mdc(Consumer> consumer) { + return Mono.subscriberContext() + .doOnNext(ctx -> { + Optional> maybeContextMap = ctx.getOrEmpty(CONTEXT_KEY); + if (maybeContextMap.isPresent()) { + consumer.accept(maybeContextMap.get()); + } else { + consumer.accept(Collections.emptyMap()); + log.warn("logger context is empty,please call publisher.subscriberContext(ReactiveLogger.mdc()) first!"); + } + }) + .then(); + } + + public static Consumer> onNext(Consumer logger) { + return on(SignalType.ON_NEXT, (ctx, signal) -> { + logger.accept(signal.get()); + }); + + } + + public static Consumer> onComplete(Runnable logger) { + return on(SignalType.ON_COMPLETE, (ctx, signal) -> { + logger.run(); + }); + } + + public static Consumer> onError(Consumer logger) { + return on(SignalType.ON_ERROR, (ctx, signal) -> { + logger.accept(signal.getThrowable()); + }); + } + + +} diff --git a/hsweb-core/src/test/java/org/hswebframework/web/logger/ReactiveLoggerTest.java b/hsweb-core/src/test/java/org/hswebframework/web/logger/ReactiveLoggerTest.java new file mode 100644 index 000000000..f03b43e63 --- /dev/null +++ b/hsweb-core/src/test/java/org/hswebframework/web/logger/ReactiveLoggerTest.java @@ -0,0 +1,33 @@ +package org.hswebframework.web.logger; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import java.time.Duration; + +import static org.junit.Assert.*; + +@Slf4j +public class ReactiveLoggerTest { + + + @Test + public void test() { + + Flux.range(0, 5) + .delayElements(Duration.ofSeconds(2)) + .flatMap(i -> ReactiveLogger.mdc("requestId", "test").thenReturn(i)) + .doOnEach(ReactiveLogger.onNext(v -> { + log.info("test:{}", v); + })) + .subscriberContext(ReactiveLogger.start("r","1")) + .as(StepVerifier::create) + .expectNextCount(5) + .verifyComplete(); + + + } + +} \ No newline at end of file diff --git a/hsweb-logging/hsweb-access-logging-aop/pom.xml b/hsweb-logging/hsweb-access-logging-aop/pom.xml index 61d6a7f90..57164a872 100644 --- a/hsweb-logging/hsweb-access-logging-aop/pom.xml +++ b/hsweb-logging/hsweb-access-logging-aop/pom.xml @@ -34,15 +34,32 @@ io.swagger swagger-annotations + org.springframework spring-webmvc + true + + + org.springframework + spring-webflux + true + + org.hswebframework.web hsweb-core ${project.version} + + + org.hswebframework.web + hsweb-authorization-api + ${project.version} + true + + javax.servlet javax.servlet-api diff --git a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupport.java b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupport.java index 1623a124f..18e721a4c 100644 --- a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupport.java +++ b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupport.java @@ -9,11 +9,13 @@ import org.hswebframework.web.logging.LoggerDefine; import org.hswebframework.web.logging.events.AccessLoggerAfterEvent; import org.hswebframework.web.logging.events.AccessLoggerBeforeEvent; import org.hswebframework.web.utils.WebUtils; +import org.reactivestreams.Publisher; import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.Ordered; import org.springframework.util.ClassUtils; +import reactor.core.publisher.Mono; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; @@ -28,9 +30,6 @@ import java.util.List; */ public class AopAccessLoggerSupport extends StaticMethodMatcherPointcutAdvisor { - @Autowired(required = false) - private final List listeners = new ArrayList<>(); - @Autowired(required = false) private final List loggerParsers = new ArrayList<>(); @@ -38,28 +37,13 @@ public class AopAccessLoggerSupport extends StaticMethodMatcherPointcutAdvisor { private ApplicationEventPublisher eventPublisher; - public AopAccessLoggerSupport addListener(AccessLoggerListener loggerListener) { - if (!listeners.contains(loggerListener)) { - listeners.add(loggerListener); - } - return this; - } - - public AopAccessLoggerSupport addParser(AccessLoggerParser parser) { - if (!loggerParsers.contains(parser)) { - loggerParsers.add(parser); - } - return this; - } - public AopAccessLoggerSupport() { setAdvice((MethodInterceptor) methodInvocation -> { MethodInterceptorHolder methodInterceptorHolder = MethodInterceptorHolder.create(methodInvocation); AccessLoggerInfo info = createLogger(methodInterceptorHolder); - Object response; + Object response = null; try { eventPublisher.publishEvent(new AccessLoggerBeforeEvent(info)); - listeners.forEach(listener -> listener.onLogBefore(info)); response = methodInvocation.proceed(); info.setResponse(response); } catch (Throwable e) { @@ -69,7 +53,6 @@ public class AopAccessLoggerSupport extends StaticMethodMatcherPointcutAdvisor { info.setResponseTime(System.currentTimeMillis()); //触发监听 eventPublisher.publishEvent(new AccessLoggerAfterEvent(info)); - listeners.forEach(listener -> listener.onLogger(info)); } return response; }); diff --git a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupportAutoConfiguration.java b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupportAutoConfiguration.java index 8286f7f1b..586268f49 100644 --- a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupportAutoConfiguration.java +++ b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/AopAccessLoggerSupportAutoConfiguration.java @@ -6,6 +6,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -21,10 +22,17 @@ import org.springframework.context.annotation.Configuration; public class AopAccessLoggerSupportAutoConfiguration { @Bean + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public AopAccessLoggerSupport aopAccessLoggerSupport() { return new AopAccessLoggerSupport(); } + @Bean + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) + public ReactiveAopAccessLoggerSupport reactiveAopAccessLoggerSupport() { + return new ReactiveAopAccessLoggerSupport(); + } + @Bean public DefaultAccessLoggerParser defaultAccessLoggerParser(){ return new DefaultAccessLoggerParser(); @@ -36,29 +44,10 @@ public class AopAccessLoggerSupportAutoConfiguration { return new SwaggerAccessLoggerParser(); } -// @Bean -// public ListenerProcessor listenerProcessor() { -// return new ListenerProcessor(); -// } -// -// public static class ListenerProcessor implements BeanPostProcessor { -// -// @Autowired -// private AopAccessLoggerSupport aopAccessLoggerSupport; -// -// @Override -// public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { -// return bean; -// } -// -// @Override -// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { -// if (bean instanceof AccessLoggerListener) { -// aopAccessLoggerSupport.addListener(((AccessLoggerListener) bean)); -// } if (bean instanceof AccessLoggerParser) { -// aopAccessLoggerSupport.addParser(((AccessLoggerParser) bean)); -// } -// return bean; -// } -// } + + @Bean + @ConditionalOnClass(name = "org.hswebframework.web.authorization.annotation.Resource") + public ResourceAccessLoggerParser resourceAccessLoggerParser(){ + return new ResourceAccessLoggerParser(); + } } diff --git a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/ReactiveAopAccessLoggerSupport.java b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/ReactiveAopAccessLoggerSupport.java new file mode 100644 index 000000000..ab04dbc09 --- /dev/null +++ b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/ReactiveAopAccessLoggerSupport.java @@ -0,0 +1,170 @@ +package org.hswebframework.web.loggin.aop; + +import org.aopalliance.intercept.MethodInterceptor; +import org.hswebframework.web.aop.MethodInterceptorHolder; +import org.hswebframework.web.id.IDGenerator; +import org.hswebframework.web.logging.RequestInfo; +import org.hswebframework.web.logging.AccessLoggerInfo; +import org.hswebframework.web.logging.AccessLoggerListener; +import org.hswebframework.web.logging.LoggerDefine; +import org.hswebframework.web.logging.events.AccessLoggerAfterEvent; +import org.hswebframework.web.logging.events.AccessLoggerBeforeEvent; +import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.ClassUtils; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.context.Context; + +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 使用AOP记录访问日志,并触发{@link AccessLoggerListener#onLogger(AccessLoggerInfo)} + * + * @author zhouhao + * @since 3.0 + */ +public class ReactiveAopAccessLoggerSupport extends StaticMethodMatcherPointcutAdvisor implements WebFilter { + + @Autowired(required = false) + private final List loggerParsers = new ArrayList<>(); + + @Autowired + private ApplicationEventPublisher eventPublisher; + + public ReactiveAopAccessLoggerSupport() { + setAdvice((MethodInterceptor) methodInvocation -> { + MethodInterceptorHolder methodInterceptorHolder = MethodInterceptorHolder.create(methodInvocation); + AccessLoggerInfo info = createLogger(methodInterceptorHolder); + Object response = methodInvocation.proceed(); + if (response instanceof Mono) { + return wrapMonoResponse(((Mono) response), info); + } else if (response instanceof Flux) { + return wrapFluxResponse(((Flux) response), info); + } + return response; + }); + } + + protected Flux wrapFluxResponse(Flux flux, AccessLoggerInfo loggerInfo) { + return Mono.subscriberContext() + .flatMap(ctx -> Mono.justOrEmpty(ctx.getOrEmpty(RequestInfo.class))) + .doOnNext(loggerInfo::putAccessInfo) + .thenMany(flux) + .doOnError(loggerInfo::setException) + .doFinally(f -> { + loggerInfo.setResponseTime(System.currentTimeMillis()); + eventPublisher.publishEvent(new AccessLoggerAfterEvent(loggerInfo)); + }); + } + + protected Mono wrapMonoResponse(Mono mono, AccessLoggerInfo loggerInfo) { + return Mono.subscriberContext() + .flatMap(ctx -> Mono.justOrEmpty(ctx.getOrEmpty(RequestInfo.class))) + .doOnNext(loggerInfo::putAccessInfo) + .then(mono) + .doOnError(loggerInfo::setException) + .doOnSuccess(loggerInfo::setResponse) + .doFinally(f -> { + loggerInfo.setResponseTime(System.currentTimeMillis()); + eventPublisher.publishEvent(new AccessLoggerAfterEvent(loggerInfo)); + }); + } + + @SuppressWarnings("all") + protected AccessLoggerInfo createLogger(MethodInterceptorHolder holder) { + AccessLoggerInfo info = new AccessLoggerInfo(); + info.setId(IDGenerator.MD5.generate()); + + info.setRequestTime(System.currentTimeMillis()); + LoggerDefine define = loggerParsers.stream() + .filter(parser -> parser.support(ClassUtils.getUserClass(holder.getTarget()), holder.getMethod())) + .findAny() + .map(parser -> parser.parse(holder)) + .orElse(null); + + if (define != null) { + info.setAction(define.getAction()); + info.setDescribe(define.getDescribe()); + } + + Map value = new ConcurrentHashMap<>(); + + String[] names = holder.getArgumentsNames(); + + Object[] args = holder.getArguments(); + + for (int i = 0; i < args.length; i++) { + String name = names[i]; + Object val = args[i]; + if (val == null) { + value.put(name, "null"); + continue; + } + if (val instanceof Mono) { + args[i] = ((Mono) val) + .doOnNext(param -> { + value.put(name, param); + }); + } else if (val instanceof Flux) { + List arr = new ArrayList<>(); + value.put(name, arr); + args[i] = ((Flux) val) + .doOnNext(param -> { + arr.add(param); + }); + } else { + value.put(name, val); + } + } + + info.setParameters(value); + info.setTarget(holder.getTarget().getClass()); + info.setMethod(holder.getMethod()); + return info; + + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + @Override + public boolean matches(Method method, Class aClass) { + return loggerParsers.stream().anyMatch(parser -> parser.support(aClass, method)); + } + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return chain + .filter(exchange) + .subscriberContext(Context.of(RequestInfo.class, createAccessInfo(exchange))); + } + + private RequestInfo createAccessInfo(ServerWebExchange exchange) { + RequestInfo info = new RequestInfo(); + ServerHttpRequest request = exchange.getRequest(); + info.setRequestId(request.getId()); + info.setPath(request.getPath().value()); + info.setRequestMethod(request.getMethodValue()); + info.setHeaders(request.getHeaders().toSingleValueMap()); + + Optional.ofNullable(request.getRemoteAddress()) + .map(InetSocketAddress::getAddress) + .map(InetAddress::getHostAddress) + .ifPresent(info::setIpAddr); + + return info; + } +} diff --git a/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/ResourceAccessLoggerParser.java b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/ResourceAccessLoggerParser.java new file mode 100644 index 000000000..11c652e9a --- /dev/null +++ b/hsweb-logging/hsweb-access-logging-aop/src/main/java/org/hswebframework/web/loggin/aop/ResourceAccessLoggerParser.java @@ -0,0 +1,54 @@ +package org.hswebframework.web.loggin.aop; + + +import org.hswebframework.web.aop.MethodInterceptorHolder; +import org.hswebframework.web.authorization.annotation.Resource; +import org.hswebframework.web.authorization.annotation.ResourceAction; +import org.hswebframework.web.logging.LoggerDefine; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.ClassUtils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +public class ResourceAccessLoggerParser implements AccessLoggerParser { + + Set> annotations = new HashSet<>(Arrays.asList( + Resource.class, ResourceAction.class + )); + + @Override + public boolean support(Class clazz, Method method) { + Set a1 = AnnotatedElementUtils.findAllMergedAnnotations(method, annotations); + Set a2 = AnnotatedElementUtils.findAllMergedAnnotations(clazz, annotations); + + + return !a1.isEmpty() || !a2.isEmpty(); + } + + @Override + public LoggerDefine parse(MethodInterceptorHolder holder) { + + Set a1 = AnnotatedElementUtils.findAllMergedAnnotations(holder.getMethod(), annotations); + Set a2 = AnnotatedElementUtils.findAllMergedAnnotations(ClassUtils.getUserClass(holder.getTarget()), annotations); + + LoggerDefine define = new LoggerDefine(); + + Stream.concat(a1.stream(), a2.stream()) + .forEach(ann -> { + if (ann instanceof ResourceAction) { + define.setAction(((ResourceAction) ann).name()); + } + if (ann instanceof Resource) { + define.setDescribe(((Resource) ann).name()); + } + }); + + return define; + } +} diff --git a/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/AccessLoggerInfo.java b/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/AccessLoggerInfo.java index df931d06a..3dd1fcbc7 100644 --- a/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/AccessLoggerInfo.java +++ b/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/AccessLoggerInfo.java @@ -1,5 +1,8 @@ package org.hswebframework.web.logging; +import lombok.Getter; +import lombok.Setter; + import java.io.PrintWriter; import java.io.Serializable; import java.io.StringWriter; @@ -16,6 +19,8 @@ import java.util.function.Function; * @author zhouhao * @since 3.0 */ +@Getter +@Setter public class AccessLoggerInfo { /** @@ -45,7 +50,7 @@ public class AccessLoggerInfo { /** * 访问对应的java类 */ - private Class target; + private Class target; /** * 请求的参数,参数为java方法的参数而不是http参数,key为参数名,value为参数值. @@ -96,122 +101,13 @@ public class AccessLoggerInfo { */ private Throwable exception; - - public String getAction() { - return action; - } - - public void setAction(String action) { - this.action = action; - } - - public String getDescribe() { - return describe; - } - - public void setDescribe(String describe) { - this.describe = describe; - } - - public Method getMethod() { - return method; - } - - public void setMethod(Method method) { - this.method = method; - } - - public Class getTarget() { - return target; - } - - public void setTarget(Class target) { - this.target = target; - } - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public Map getHttpHeaders() { - return httpHeaders; - } - - public void setHttpHeaders(Map httpHeaders) { - this.httpHeaders = httpHeaders; - } - - public String getHttpMethod() { - return httpMethod; - } - - public void setHttpMethod(String httpMethod) { - this.httpMethod = httpMethod; - } - - public Object getResponse() { - return response; - } - - public void setResponse(Object response) { - this.response = response; - } - - public long getRequestTime() { - return requestTime; - } - - public void setRequestTime(long requestTime) { - this.requestTime = requestTime; - } - - public long getResponseTime() { - return responseTime; - } - - public void setResponseTime(long responseTime) { - this.responseTime = responseTime; - } - - public Throwable getException() { - return exception; - } - - public void setException(Throwable exception) { - this.exception = exception; - } - - public Map toSimpleMap(Function noSerialExchange) { - return toSimpleMap(noSerialExchange, new LinkedHashMap<>()); - } - public Map toSimpleMap(Function objectFilter, Map map) { map.put("action", action); map.put("describe", describe); if (method != null) { StringJoiner methodAppender = new StringJoiner(",", method.getName().concat("("), ")"); - String[] parameterNames = parameters.keySet().toArray(new String[parameters.size()]); - Class[] parameterTypes = method.getParameterTypes(); + String[] parameterNames = parameters.keySet().toArray(new String[0]); + Class[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { methodAppender.add(parameterTypes[i].getSimpleName().concat(" ").concat(parameterNames.length > i ? parameterNames[i] : ("arg" + i))); @@ -244,11 +140,11 @@ public class AccessLoggerInfo { return map; } - public String getId() { - return id; - } - public void setId(String id) { - this.id = id; + public void putAccessInfo(RequestInfo info){ + setIp(info.getIpAddr()); + setHttpMethod(info.getRequestMethod()); + setHttpHeaders(info.getHeaders()); + setUrl(info.getPath()); } } diff --git a/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/LoggerDefine.java b/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/LoggerDefine.java index a14d82ff1..dabfa1889 100644 --- a/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/LoggerDefine.java +++ b/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/LoggerDefine.java @@ -1,30 +1,18 @@ package org.hswebframework.web.logging; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor public class LoggerDefine { private String action; private String describe; - - public LoggerDefine(String action,String describe){ - this.action=action; - this.describe=describe; - } - - public String getDescribe() { - return describe; - } - - public void setAction(String action) { - this.action = action; - } - - public String getAction() { - return action; - } - - public void setDescribe(String describe) { - this.describe = describe; - } } diff --git a/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/RequestInfo.java b/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/RequestInfo.java new file mode 100644 index 000000000..fae52cee9 --- /dev/null +++ b/hsweb-logging/hsweb-access-logging-api/src/main/java/org/hswebframework/web/logging/RequestInfo.java @@ -0,0 +1,25 @@ +package org.hswebframework.web.logging; + +import lombok.*; + +import java.util.Map; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RequestInfo { + + private String requestId; + + private String ipAddr; + + private String path; + + private String requestMethod; + + private Map headers; + + +}