I made a functionality that prevents multiple-login for one username at the same time and I call it in Actions like this:
int userId = (int)WebSecurity.CurrentUserId;
if ((this.Session.SessionID != dba.getSessionId(userId)) || dba.getSessionId(userId) == null)
{
WebSecurity.Logout();
return RedirectToAction("Index", "Home");
}
So the point is that every time user logins I save his sessionID into database field. So if someone with same username logins over someone already logged in with same username it overwrites that database field with this new session. If sessionID in DB is not the same as current session ID of logined user it log him out.
Is there a possibility to put this part of code in 1 place or do I have to put it in every single Action in my application?
I tried in Global.asax:
void Application_BeginRequest(object sender, EventArgs e)
{
if (Session["ID"] != null)
{
int userId = Convert.ToInt32(Session["ID"]);
if ((this.Session.SessionID != db.getSessionId(userId)) || db.getSessionId(userId) == null)
{
WebSecurity.Logout();
}
}
}
But I can't use Session here nor WebSecurity class if I try like this:
void Application_BeginRequest(object sender, EventArgs e)
{
int userId = (int)WebSecurity.CurrentUserId;
if ((this.Session.SessionID != db.getSessionId(userId)) || db.getSessionId(userId) == null)
{
WebSecurity.Logout();
Response.RedirectToRoute("Default");
}
}
because I get null reference exception.
EDIT
I used this:
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
int userId = (int)WebSecurity.CurrentUserId;
using (var db = new UsersContext())
{
string s = db.getSessionId(userId);
if ((filterContext.HttpContext.Session.SessionID != db.getSessionId(userId)) || db.getSessionId(userId) == null)
{
WebSecurity.Logout();
filterContext.Result = new RedirectResult("/Home/Index");
}
}
}
I had to use using statement for context, otherwise db.getSessionId(userId) was returning old sessionId. Method is this:
public string getSessionId(int userId)
{
string s = "";
var get = this.UserProfiles.Single(x => x.UserId == userId);
s = get.SessionId;
return s;
}
Very strange, will have to read about why that happened.
Everything works fine, except one thing. I have one JsonResult action in a controller, which returns Json, but since event(its textbox on enter event) can't trigger POST(I assume it's because it logs out before) redirect doesn't work. It can't even post to that Json action to receive callback and redirect. Any clues on that?
success: function (data) {
if (data.messageSaved) {
//data received - OK!
}
else {
// in case data was not received, something went wrong redirect out
window.location.href = urlhome;
}
}
Before I used ActionFilterAttribute I used code to check different sessions inside of POST and of course it could make callback and therefore redirect if didn't receive the data.. But now since it can't even POST and go into method it just stucks there and doesn't redirect :)
I would derive from AuthorizeAttribute. No need to check this information if you don't need to authorize the request.
public class SingleLoginAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
int userId = (int)WebSecurity.CurrentUserId;
if ((filterContext.HttpContext.Session.SessionID != dba.getSessionId(userId))
|| dba.getSessionId(userId) == null)
{
WebSecurity.Logout();
isAuthorized = false;
filterContext.Result = new RedirectResult("/Home/Index");
}
}
return isAuthorized;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult()
{
Data = FormsAuthentication.LoginUrl,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
I'd also mention that this allows you to short circuit other ActionFilters because they run after OnAuthorization.
Then as Rob Lyndon mentioned, you could in the FilterConfig (MVC4)
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new SingleLoginAuthorizeAttribute());
}
}
Then when you don't want to require any authorization, you can use the AllowAnonymouseAttribute on your ActionResult methods or Controller Class to allow anonymous access.
Update
I added a way for your ajax calls (Get or Post) to work with timeouts. You can do something like:
success: function (jsonResult)
{
if (jsonResult.indexOf('http') == 0)
{
window.location = jsonResult;
}
// do other stuff with the Ajax Result
}
This isn't exactly the best way, but if you want more information on how to do this better I would ask another question instead of appending more questions on this one.
The ActionFilterAttribute
is the way to go.
We created an Action Filter called SeatCheck
and decorate each controller like this:
[SeatCheck]
public class NoteController : BaseController
{
We use that to get a count of seats and other functions, but it makes it so much easier to control everywhere without thinking about it.
In the proejct ActionFilters
folder we have the SeatCheck.cs
file that looks like this:
namespace site.ActionFilters
{
public class SeatCheckAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
You can get the SessionID in the Action Filter like this
filterContext.HttpContext.Session.SessionID
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With