In my spring project I have such an aspect class for logging
@Aspect
@Component
public class BaseLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(BaseLoggingAspect.class);
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface NonLoggingField {
}
@Pointcut("execution(public * *(..))")
private void allPublicMethods() {
}
@Pointcut("within(img.imaginary.service.*)")
private void inServices() {
}
@Pointcut("within(img.imaginary.dao.*)")
private void inDao() {
}
@Before("allPublicMethods() && inServices() || inDao()")
public void logBeforeCall(JoinPoint joinPoint) {
if (logger.isDebugEnabled()) {
logger.debug("begin method {} in {} class with arguments: {}", joinPoint.getSignature().getName(),
joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getArgs());
}
}
}
this aspect simply catches all the public methods of the service and dao layers and outputs to the log at the beginning of execution the name of the method, the name of the class, and the masi of the values of the arguments of the method
in this aspect, I created a NonLoggingField annotation that I want to apply to some fields of classes of those objects that can be passed to the parameters of these logged methods, for example this:
public class User {
@NonLoggingField
public String userEmail;
public name;
public User(String userEmail, String name) {
this.userEmail = userEmail;
this.name= name;
}
public String tiString() {
return String.format("user name: %s and his email: %s", name, userEmail);
}
}
the fact is that such objects will be written to the log through its toString method, but it is necessary that the email somehow does not get into the log using the notLoggingField annotation, while there are thoughts in my head to do through reflection, but there is no clarity how to do this without over difficult code using reflection, especially considering that objects may have objects of other types inside, which may have the same fields with annotations or collections with objects with such fields. perhaps the AspectJ library can help, but I can't find such mechanisms in it. Please help me come up with something
CodePudding user response:
During runtime, a method parameter is just a value. The JVM does not know at this point if the caller called the method using constants, literals, fields or results of other method calls. That kind of information, you only see in the source code. In byte code, whatever dereferencing operation or computation necessary to determine the parameter's value (or a reference to the corresponding object) is done before calling the method. So there is no connection to the field annotation.
Would annotating method parameters be an alternative for you?
If your requirement is very specific, e.g. intercept field accesses from toString
methods and return dummy values instead, if the field is annotated, that would be possible. But this would not be fool-proof. Imagine for example that toString
calls a getter method instead of directly accessing the field or that a method other than toString
logs the field. You do not always want to falisfy the field value on read access, because other parts of the application might rely on it working correctly. Not every toString
call is made in order to log something.
I think you should solve the problem in another way, e.g. by applying filter rules for the logging tool you use. Or if you really want solve it at the application level, you could create an interface like
public interface PrivacyLogger {
String toStringSensitive();
}
and make each class containing sensitive information implement that interface. The logging aspect could then for each printed object determine if it is instanceof toStringSensitive()
. If so, it would log the result of toStringSensitive()
instead of toString()
, i.e. in the simplest case something like
Object toBeLogged = whatever();
logger.log(
toBeLogged instanceof PrivacyLogger
? ((PrivacyLogger) toBeLogged).toStringSensitive()
: toBeLogged
);
Of course, you need to iterate over getArgs()
and determine the correct log string for each object. Probably, you want to write a utility method doing that for the whole parameters array.
Moreover, in a complex class, the toStringSensitive()
implementation should of course also check if its own fields are PrivacyLogger
instances and in that case fold the values of their resapctive toStringSensitive()
methods into itw own, so that it works recursively.
I am sorry I have no better news for you, but privacy is something which needs too be built into an application from the ground. There is no simple, fool-proof way to do that with one little aspect. The aspect can utilise the existing application infrastructure and avoid scattering and tangling, but it cannot decide on its own what needs to be prohibited from getting logged and what not.