Ability to reuse an account with one user at a time

I created a functionality that simultaneously prevents multiple logins for a single username, and I call it in Actions as follows:

int userId = (int)WebSecurity.CurrentUserId; if ((this.Session.SessionID != dba.getSessionId(userId)) || dba.getSessionId(userId) == null) { WebSecurity.Logout(); return RedirectToAction("Index", "Home"); } 

So, the fact is that every time a user logs in, I save his sessionID in the database field. Therefore, if someone with the same username logs in through someone who is already logged in with the same username, he overwrites this database field with this new session. If the sessionID in the database does not match the current session ID for the registered user, exit it.

Is it possible to put this part of the code in 1 place or do I need to put it in every 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 cannot use Session here and the 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 a link 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 the using statement for context, otherwise db.getSessionId (userId) returned the old sessionId. The method is as follows:

  public string getSessionId(int userId) { string s = ""; var get = this.UserProfiles.Single(x => x.UserId == userId); s = get.SessionId; return s; } 

Very strange, you will need to read why this happened.

Everything works fine except for one. I have one JsonResult action in the controller that returns Json, but since the event (its text box on the input of the event) cannot trigger POST (I assume because it logs out earlier), the redirect does not work. He can't even post this Json action to receive callback and redirect. Any clues about this?

  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 the code to check for different sessions inside POST and, of course, it could make a callback and therefore redirect if it didn't receive the data. But now, since it cannot even POST and go to the method, it just keeps there and does not redirect :)

+6
source share
6 answers

I would get an AuthorizeAttribute . You do not need to verify this information unless you 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 would also mention that this allows you to short-circuit other ActionFilters, because they run after OnAuthorization.

  • Forward Order - OnAuthorization: AuthorizationFilter (Scope Controller)
  • Direct Order - OnActionExecuting: ActionFilter1 (Scope Global)
  • Direct Order - OnActionExecuting: ActionFilter2 (Scope Controller)
  • Direct Order - OnActionExecuting: ActionFilter3 (action with scope)

Then, as Rob Lyndon said, you can in FilterConfig (MVC4)

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

Then, when you do not want to require authorization, you can use AllowAnonymouseAttribute in the ActionResult methods or controller class to allow anonymity 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 is not exactly the best way, but if you would like more information on how to do it better, I would ask another question, instead of adding additional questions about this.

+4
source

ActionFilterAttribute is the way to go.

+3
source

We created an Action Filter called SeatCheck and decorated each controller as follows:

 [SeatCheck] public class NoteController : BaseController { 

We use this to get the number of seats and other functions, but it makes it much easier to control everywhere without thinking about it.

In the proejct ActionFilters folder, we have the SeatCheck.cs file, which looks like this:

 namespace site.ActionFilters { public class SeatCheckAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { 

You can get a SessionID in an Action Filter like this

  filterContext.HttpContext.Session.SessionID 
+2
source

Yes there really is. You can use the attribute obtained from ActionFilterAttribute .

I would write a class called SessionSecurityAttribute :

 public class SessionSecurityAttribute : ActionFilterAttribute { public MyDbConn MyDbConn { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { var session = filterContext.RequestContext.HttpContext.Session; if (session["ID"] != null && WebSecurity.IsAuthenticated) { int userId = Convert.ToInt32(session["ID"]); if ((sessionID != MyDbConn.getSessionId(userId)) || MyDbConn.getSessionId(userId) == null) { WebSecurity.Logout(); } } } } 

The question remains: how can you add these attributes to your actions by giving them access to your database? It's easy: in Global.asax you can call the RegisterGlobalFilters bootstrap method:

 public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new SessionSecurityAttribute { MyDbConn = DependencyResolver.Current.GetService<MyDbConn>() }); } 

This adds your SessionSecurityAttribute , complete with a DB connection, to each default action without a line of repeating code.

+1
source

Create a custom action filter and put this code in the filter, and then apply the filter to the controller.

+1
source

You can try to implement your own custom ISessionIDManager: http://msdn.microsoft.com/en-us/library/system.web.sessionstate.isessionidmanager.aspx

In the confirmation, check if it is valid, otherwise return false.

0
source

Source: https://habr.com/ru/post/951357/


All Articles