kezhenxu94
1 year ago
committed by
GitHub
48 changed files with 6 additions and 610 deletions
@ -1,35 +0,0 @@ |
|||||||
/* |
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
||||||
* contributor license agreements. See the NOTICE file distributed with |
|
||||||
* this work for additional information regarding copyright ownership. |
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
||||||
* (the "License"); you may not use this file except in compliance with |
|
||||||
* the License. You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package org.apache.dolphinscheduler.api.aspect; |
|
||||||
|
|
||||||
import java.lang.annotation.Documented; |
|
||||||
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) |
|
||||||
@Documented |
|
||||||
public @interface AccessLogAnnotation { |
|
||||||
|
|
||||||
// ignore request args
|
|
||||||
String[] ignoreRequestArgs() default {"loginUser"}; |
|
||||||
|
|
||||||
boolean ignoreRequest() default false; |
|
||||||
} |
|
@ -1,178 +0,0 @@ |
|||||||
/* |
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
||||||
* contributor license agreements. See the NOTICE file distributed with |
|
||||||
* this work for additional information regarding copyright ownership. |
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
||||||
* (the "License"); you may not use this file except in compliance with |
|
||||||
* the License. You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package org.apache.dolphinscheduler.api.aspect; |
|
||||||
|
|
||||||
import org.apache.dolphinscheduler.api.metrics.ApiServerMetrics; |
|
||||||
import org.apache.dolphinscheduler.common.constants.Constants; |
|
||||||
import org.apache.dolphinscheduler.common.utils.CodeGenerateUtils; |
|
||||||
import org.apache.dolphinscheduler.dao.entity.User; |
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils; |
|
||||||
|
|
||||||
import java.lang.reflect.Method; |
|
||||||
import java.util.Arrays; |
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.Set; |
|
||||||
import java.util.regex.Matcher; |
|
||||||
import java.util.regex.Pattern; |
|
||||||
import java.util.stream.Collectors; |
|
||||||
import java.util.stream.IntStream; |
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest; |
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j; |
|
||||||
|
|
||||||
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; |
|
||||||
|
|
||||||
@Aspect |
|
||||||
@Component |
|
||||||
@Slf4j |
|
||||||
public class AccessLogAspect { |
|
||||||
|
|
||||||
private static final String TRACE_ID = "traceId"; |
|
||||||
|
|
||||||
public static final String sensitiveDataRegEx = "(password=[\'\"]+)(\\S+)([\'\"]+)"; |
|
||||||
|
|
||||||
private static final Pattern sensitiveDataPattern = Pattern.compile(sensitiveDataRegEx, Pattern.CASE_INSENSITIVE); |
|
||||||
|
|
||||||
@Pointcut("@annotation(org.apache.dolphinscheduler.api.aspect.AccessLogAnnotation)") |
|
||||||
public void logPointCut() { |
|
||||||
// Do nothing because of it's a pointcut
|
|
||||||
} |
|
||||||
|
|
||||||
@Around("logPointCut()") |
|
||||||
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { |
|
||||||
long startTime = System.currentTimeMillis(); |
|
||||||
|
|
||||||
String URI = null; |
|
||||||
String requestMethod = null; |
|
||||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
|
||||||
if (attributes != null) { |
|
||||||
HttpServletRequest request = attributes.getRequest(); |
|
||||||
URI = request.getRequestURI(); |
|
||||||
requestMethod = request.getMethod(); |
|
||||||
} |
|
||||||
|
|
||||||
// fetch AccessLogAnnotation
|
|
||||||
MethodSignature sign = (MethodSignature) proceedingJoinPoint.getSignature(); |
|
||||||
Method method = sign.getMethod(); |
|
||||||
AccessLogAnnotation annotation = method.getAnnotation(AccessLogAnnotation.class); |
|
||||||
|
|
||||||
String traceId = String.valueOf(CodeGenerateUtils.getInstance().genCode()); |
|
||||||
|
|
||||||
int userId = -1; |
|
||||||
String userName = "NOT LOGIN"; |
|
||||||
|
|
||||||
// log request
|
|
||||||
if (!annotation.ignoreRequest()) { |
|
||||||
if (attributes != null) { |
|
||||||
HttpServletRequest request = attributes.getRequest(); |
|
||||||
String traceIdFromHeader = request.getHeader(TRACE_ID); |
|
||||||
if (StringUtils.isNotEmpty(traceIdFromHeader)) { |
|
||||||
traceId = traceIdFromHeader; |
|
||||||
} |
|
||||||
// handle login info
|
|
||||||
User loginUser = parseLoginInfo(request); |
|
||||||
if (loginUser != null) { |
|
||||||
userName = loginUser.getUserName(); |
|
||||||
userId = loginUser.getId(); |
|
||||||
} |
|
||||||
|
|
||||||
// handle args
|
|
||||||
String argsString = parseArgs(proceedingJoinPoint, annotation); |
|
||||||
// handle sensitive data in the string
|
|
||||||
argsString = handleSensitiveData(argsString); |
|
||||||
log.info("REQUEST TRACE_ID:{}, LOGIN_USER:{}, URI:{}, METHOD:{}, HANDLER:{}, ARGS:{}", |
|
||||||
traceId, |
|
||||||
userName, |
|
||||||
request.getRequestURI(), |
|
||||||
request.getMethod(), |
|
||||||
proceedingJoinPoint.getSignature().getDeclaringTypeName() + "." |
|
||||||
+ proceedingJoinPoint.getSignature().getName(), |
|
||||||
argsString); |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Object ob = proceedingJoinPoint.proceed(); |
|
||||||
|
|
||||||
long costTime = System.currentTimeMillis() - startTime; |
|
||||||
log.info("Call {}:{} success, cost: {}ms", requestMethod, URI, costTime); |
|
||||||
|
|
||||||
if (userId != -1) { |
|
||||||
ApiServerMetrics.recordApiResponseTime(costTime, userId); |
|
||||||
} |
|
||||||
|
|
||||||
return ob; |
|
||||||
} |
|
||||||
|
|
||||||
private String parseArgs(ProceedingJoinPoint proceedingJoinPoint, AccessLogAnnotation annotation) { |
|
||||||
Object[] args = proceedingJoinPoint.getArgs(); |
|
||||||
String argsString = Arrays.toString(args); |
|
||||||
if (annotation.ignoreRequestArgs().length > 0) { |
|
||||||
String[] parameterNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames(); |
|
||||||
if (parameterNames.length > 0) { |
|
||||||
Set<String> ignoreSet = Arrays.stream(annotation.ignoreRequestArgs()).collect(Collectors.toSet()); |
|
||||||
HashMap<String, Object> argsMap = new HashMap<>(); |
|
||||||
|
|
||||||
for (int i = 0; i < parameterNames.length; i++) { |
|
||||||
if (!ignoreSet.contains(parameterNames[i])) { |
|
||||||
argsMap.put(parameterNames[i], args[i]); |
|
||||||
} |
|
||||||
} |
|
||||||
argsString = argsMap.toString(); |
|
||||||
} |
|
||||||
} |
|
||||||
return argsString; |
|
||||||
} |
|
||||||
|
|
||||||
protected String handleSensitiveData(String originalData) { |
|
||||||
Matcher matcher = sensitiveDataPattern.matcher(originalData.toLowerCase()); |
|
||||||
IntStream stream = IntStream.builder().build(); |
|
||||||
boolean exists = false; |
|
||||||
while (matcher.find()) { |
|
||||||
if (matcher.groupCount() == 3) { |
|
||||||
stream = IntStream.concat(stream, IntStream.range(matcher.end(1), matcher.end(2))); |
|
||||||
exists = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (exists) { |
|
||||||
char[] chars = originalData.toCharArray(); |
|
||||||
stream.forEach(idx -> { |
|
||||||
chars[idx] = '*'; |
|
||||||
}); |
|
||||||
return new String(chars); |
|
||||||
} |
|
||||||
|
|
||||||
return originalData; |
|
||||||
} |
|
||||||
|
|
||||||
private User parseLoginInfo(HttpServletRequest request) { |
|
||||||
User loginUser = (User) (request.getAttribute(Constants.SESSION_USER)); |
|
||||||
return loginUser; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
/* |
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
||||||
* contributor license agreements. See the NOTICE file distributed with |
|
||||||
* this work for additional information regarding copyright ownership. |
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
||||||
* (the "License"); you may not use this file except in compliance with |
|
||||||
* the License. You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package org.apache.dolphinscheduler.api.aspect; |
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions; |
|
||||||
import org.junit.jupiter.api.Test; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Hua Jiang |
|
||||||
*/ |
|
||||||
|
|
||||||
public class AccessLogAspectTest { |
|
||||||
|
|
||||||
private AccessLogAspect accessLogAspect = new AccessLogAspect(); |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testHandleSensitiveData() { |
|
||||||
String data = |
|
||||||
"userPassword='7ad2410b2f4c074479a8937a28a22b8f', email='xxx@qq.com', database='null', userName='root', password='root', other='null'"; |
|
||||||
String expected = |
|
||||||
"userPassword='********************************', email='xxx@qq.com', database='null', userName='root', password='****', other='null'"; |
|
||||||
|
|
||||||
String actual = accessLogAspect.handleSensitiveData(data); |
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
Loading…
Reference in new issue