C#.net核心,没有敏感信息的日志请求

时间:2019-03-11 14:30:37

标签: c#

使用.net core 2.1 C#7.1。

我有一个API REST服务器。

我有以下课程:

public class ActionTimingAttribute : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // do something before the action executes
        DateTime execStart = DateTime.UtcNow;

        var request = context.HttpContext.Request;
        string payload = ""; // HERE I NEED THE PAYLOAD WITHOUT SENSITIVE DATA

        //doing funky stuff

        await next();

        DateTime execEnd = DateTime.UtcNow;
        double elapsed = (execEnd - execStart).TotalMilliseconds;

        Log(payload, elapsed);
    }
}

现在,我可以将payloadnew StreamReader(request.Body)一起使用,其余的.. 但是,例如,如果我有密码字段,则我不希望payload具有此值。例如,我想将值更改为“ #####”。

我有很多请求和不同请求。 对于每个POST请求,我都有一个代表接收到的json的类...

显然我不在乎HttpGet。

服务器中的方法看起来像这样(一个仅用于登录的示例。)

[HttpPost, Route("Auth/Login")]
public async Task<Responses.Login> Login (Requests.Login request)

我假设我需要某种属性,所以它看起来像:

public class Login
{
    public string Email {set;get;}

    [SensitiveData]
    public string Password {set;get;}

}

但是在这里,我无法在有有效载荷的地方做最后的组合,并且我想省略任何sensitiveData。

如果我有请求模型,则可以查看其字段并检查它们是否敏感,是否可以更改模型中的值(假设它不会损害控制器实际接收的模型)。

谢谢。

1 个答案:

答案 0 :(得分:2)

这是使用自定义NewtonSoft序列化的解决方案。反射代码可能还有一些改进的空间。

public class SensitiveDataAttribute: Attribute
{
}

public sealed class SensitiveDataJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        foreach (PropertyInfo prop in value.GetType().GetProperties())
        {
            object[] attrs = prop.GetCustomAttributes(true);
            if (attrs.Any(x => x is SensitiveDataAttribute))
                prop.SetValue(value, "#####");
        }

        var t = JToken.FromObject(value);
        if (t.Type != JTokenType.Object)
        {
            t.WriteTo(writer);
        }
        else
        {
            JObject o = (JObject)t;
            o.WriteTo(writer);
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanRead => false;

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

这是它的用法。

public class MyObject
{
    [SensitiveData]
    public string Password { get; set; }

    public string UserName { get; set; }
}

ActionTimingAttribute : IAsyncActionFilter

 private static readonly SensitiveDataJsonConverter SensitiveDataConverter = new SensitiveDataJsonConverter();

 public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
 {

     string safePayload;
     if (context.ActionArguments.Count == 1)
     {
         var safeObject = context.ActionArguments[context.ActionArguments.Keys.ElementAt(0)];
          safePayload = JsonConvert.SerializeObject(safeObject, Formatting.None, SensitiveDataConverter);
     }
     else
         safePayload = request.StoreAndGetPayload();



 // the rest..
}

要处理不更改原件的情况,您可以按照以下说明进行克隆:Deep cloning objects

public static class ObjectCopier
{
    public static T CloneJson<T>(this T source)
    {
        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        // initialize inner objects individually
        // for example in default constructor some list property initialized with some values,
        // but in 'source' these items are cleaned -
        // without ObjectCreationHandling.Replace default constructor values will be added to result
        var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
    }
}

您的用法将如下所示:

safePayload = JsonConvert.SerializeObject(safeObject.CloneJson(), Formatting.None, SensitiveDataConverter);