Search code examples
c#asp.netasp.net-mvcasp.net-mvc-5user-activity

How to Track Anonymous Users' Activities in ASP.Net MVC 5?


I create an ASP.Net MVC 5 web application whose service can be used by anonymous users. When the anonymous user uses the web service, it will do some query from the database. Due to security reason, however, my client wants to track "suspicious" activities of anonymous users. One of them includes how many times an anonymous user queries per day (for preventing significant amount of data being "stolen").

Is there any way to capture this information?

For registered user, we could create additional property in the ApplicationUser called QueryNo and add it in the Claim like this:

public class ApplicationUser : IdentityUser {
    public uint QueryNo { get; set; } //how many times this user has queried

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        userIdentity.AddClaim(new Claim("QueryNo", QueryNo));
        return userIdentity;
    }
}

And when we want to track its activity, we could simply increase its QueryNo per query activity. When we want to display it, we could, for example, simply define an extension for Identity like this:

public static class IdentityExtensions {
    public static string GetQueryNo(this IIdentity identity) {
        if (identity == null) {
            throw new ArgumentNullException("identity");
        }
        var ci = identity as ClaimsIdentity;
        if (ci != null) {
            return ci.FindFirstValue("QueryNo");
        }
        return null;
    }
}

And then simply use it in the view like this:

<p>No Of Query: @User.Identity.GetQueryNo()</p>

But how do we track anonymous user's activities (i.e. such as its no of queries)?


Solution

  • First create your action filter:

    public class TrackingActionFilter : ActionFilterAttribute
    {
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var sessionId = filterContext.HttpContext.Session.SessionID;
            Debug.WriteLine("Printing session Id: " + sessionId);
    
            var ip = filterContext.HttpContext.Request.UserHostAddress;
            Debug.WriteLine("Printing ip: " + ip);
    
            var headers = filterContext.RequestContext.HttpContext.Request.Headers;
            foreach(var header in headers) {
                Debug.WriteLine("Printing header: " + header);
            }
    
            var parms = filterContext.HttpContext.Request.Params;
            foreach (var key in parms.AllKeys)
            {
                Debug.WriteLine("Printing parameter: " + key + " - " + parms[key]);
            }
    
            var routeDataKeys = filterContext.RouteData.Values.Keys;
            foreach(var key in routeDataKeys)
            {
                Debug.WriteLine("Printing route data value: " + key + " - " + filterContext.RouteData.Values[key]);
            }
    
            //Stolen with love from http://stackoverflow.com/questions/12938621/how-can-i-log-all-query-parameters-in-an-action-filter
            var stream = filterContext.HttpContext.Request.InputStream;
            var data = new byte[stream.Length];
            stream.Read(data, 0, data.Length);
            Debug.WriteLine(Encoding.UTF8.GetString(data));
        }
    }
    

    Obivously you would capture the relevant details instead of just writing them to the debug window.

    Now you can apply the action filter at the action level:

    [TrackingActionFilter]
    public ActionResult Index()
    

    Or at the controller level:

    [TrackingActionFilter]
    public class HomeController : Controller
    

    Or you can cover your entire MVC application globally via the FilterConfig:

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            filters.Add(new TrackingActionFilter());
        }
    }