Pregunta Cuando el mismo ID de usuario intenta iniciar sesión en varios dispositivos, ¿cómo puedo cancelar la sesión en el otro dispositivo?


Lo que quiero hacer es limitar un ID de usuario para que solo pueda iniciar sesión en un dispositivo a la vez. Por ejemplo, el ID de usuario "abc" inicia sesión en su computadora. El ID de usuario "abc" ahora intenta iniciar sesión desde su teléfono. Lo que quiero que suceda es matar la sesión en su computadora.

La aplicación Spotify hace exactamente esto: Spotify solo permite el inicio de sesión de un ID de usuario en un dispositivo a la vez.

Estoy usando membresía ASP.NET (SqlMembershipProvider) y Autenticación de formularios.

He experimentado con las variables de Sesión, pero no estoy seguro de a dónde ir desde aquí.


32
2018-04-09 13:40


origen


Respuestas:


Se me ocurrió una solución bastante impresionante para esto. Lo que implementé fue cuando el usuario "Bob" inicia sesión desde su PC, y luego el mismo usuario "Bob" inicia sesión desde otra ubicación, el inicio de sesión desde la primera ubicación (su PC) se eliminará mientras permite la segunda iniciar sesión para vivir. Una vez que un usuario inicia sesión, inserta un registro en una tabla personalizada que creé llamada "Inicios de sesión". Tras un inicio de sesión exitoso, se insertará un registro en esta tabla con los valores de "UserId, SessionId y LoggedIn". UserId es bastante autoexplicativo, SessionId es el ID de sesión actual (se explica a continuación cómo obtenerlo) y LoggedIn es simplemente un booleano que inicialmente se establece en True cuando el usuario inicia sesión correctamente. Coloco esta lógica de "inserción" dentro de mi método de inicio de sesión de mi AccountController luego de la validación exitosa del usuario; vea a continuación:

Logins login = new Logins();
login.UserId = model.UserName;
login.SessionId = System.Web.HttpContext.Current.Session.SessionID;;
login.LoggedIn = true;

LoginsRepository repo = new LoginsRepository();
repo.InsertOrUpdate(login);
repo.Save();

Para mi situación, quiero colocar el cheque en cada uno de mis controladores para ver si el usuario actualmente conectado está conectado a otra parte, y si es así, matar a la otra sesión (s). Luego, cuando la sesión eliminada intente navegar en cualquier lugar donde realice estas comprobaciones, las cerrará y las redireccionará a la pantalla de inicio de sesión.

Tengo tres métodos principales que hacen estas verificaciones:

IsYourLoginStillTrue(UserId, SessionId);
IsUserLoggedOnElsewhere(UserId, SessionId);
LogEveryoneElseOut(UserId, SessionId);

Guardar ID de sesión en la sesión ["..."]

Antes de todo esto, guardo el SessionID en la colección Session dentro del AccountController, dentro del Login ([HttpPost]) método:

if (Membership.ValidateUser(model.UserName, model.Password))
{
     Session["sessionid"] = System.Web.HttpContext.Current.Session.SessionID;
...

Código de controlador

Luego coloco la lógica dentro de mis controladores para controlar el flujo de la ejecución de estos tres métodos. Observe a continuación que si por alguna razón Session["sessionid"] es null, simplemente le asignará un valor de "vacío". Esto es por si acaso por alguna razón vuelve como nulo:

public ActionResult Index()
{
    if (Session["sessionid"] == null)
        Session["sessionid"] = "empty";

    // check to see if your ID in the Logins table has LoggedIn = true - if so, continue, otherwise, redirect to Login page.
    if (OperationContext.IsYourLoginStillTrue(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString()))
    {
        // check to see if your user ID is being used elsewhere under a different session ID
        if (!OperationContext.IsUserLoggedOnElsewhere(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString()))
        {
            return View();
        }
        else
        {
            // if it is being used elsewhere, update all their Logins records to LoggedIn = false, except for your session ID
            OperationContext.LogEveryoneElseOut(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString());
            return View();
        }
    }
    else
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Login", "Account");
    }
}

Los tres métodos

Estos son los métodos que utilizo para verificar si USTED aún está conectado (es decir, asegúrese de que no haya sido expulsado por otro intento de inicio de sesión), y de ser así, verifique si su identificación de usuario está registrada en otro sitio. , y si es así, ponlos en marcha simplemente configurando su estado de sesión iniciada en false en la tabla de Inicios.

public static bool IsYourLoginStillTrue(string userId, string sid)
{
    CapWorxQuikCapContext context = new CapWorxQuikCapContext();

    IEnumerable<Logins> logins = (from i in context.Logins
                                  where i.LoggedIn == true && i.UserId == userId && i.SessionId == sid
                                  select i).AsEnumerable();
    return logins.Any();
}

public static bool IsUserLoggedOnElsewhere(string userId, string sid)
{
    CapWorxQuikCapContext context = new CapWorxQuikCapContext();

    IEnumerable<Logins> logins = (from i in context.Logins
                                  where i.LoggedIn == true && i.UserId == userId && i.SessionId != sid
                                  select i).AsEnumerable();
    return logins.Any();
}

public static void LogEveryoneElseOut(string userId, string sid)
{
    CapWorxQuikCapContext context = new CapWorxQuikCapContext();

    IEnumerable<Logins> logins = (from i in context.Logins 
                                  where i.LoggedIn == true && i.UserId == userId && i.SessionId != sid // need to filter by user ID
                                  select i).AsEnumerable();

    foreach (Logins item in logins)
    {
        item.LoggedIn = false;
    }

    context.SaveChanges();
}

EDITAR También quiero agregar que este código ignora la capacidad de la función "Recordarme". Mi requisito no involucraba esta característica (de hecho, mi cliente no quería usarla, por razones de seguridad) así que simplemente lo dejé fuera. Sin embargo, con una codificación adicional, estoy bastante seguro de que esto podría tomarse en consideración.


32
2018-04-11 02:51



Tendrá que almacenar la información que alguien ha iniciado sesión en la base de datos. Esto le permitiría verificar si el usuario ya tiene una sesión existente. Fuera de la caja, el módulo de autenticación de formularios en ASP.NET funciona con cookies y no hay manera de que usted sepa en el servidor si el usuario tiene cookies en otros dispositivos a menos que, por supuesto, almacene esta información en el servidor.


7
2018-04-09 13:44



Lo que probablemente desee hacer es cuando un usuario inicia sesión, guarda su ID de sesión en la base de datos en alguna parte. Luego, en cada página a la que accede, debe verificar si la identificación de la sesión actual es la misma que la almacenada en la base de datos, y si no la cierra.

Probablemente quiera crear un controlador base que haga esto en los métodos OnAuthorization o OnActionExecuting. Otra opción sería crear su propio filtro de Autorización (preferiría que yo mismo, en realidad, ya que no me gustan las clases base comunes como esa).

En ese método, accedería a la base de datos y verificaría la identificación de la sesión.

Tenga en cuenta que el suyo no es infalible. Es posible que alguien copie la cookie de la sesión y solucione esto, aunque eso es lo suficientemente oscuro como para que la mayoría de la gente probablemente no sepa cómo hacerlo, y lo suficientemente molesto como para que los que sí lo hagan no se molesten.

También podría usar la dirección IP, pero ese es el mismo trato. Dos personas detrás de un firewall proxy o nat parecen ser el mismo usuario.


5
2018-04-09 13:47



Me gustaría señalar que un motivo clave para configurar Session ["SessionID"] = "anything" es que hasta que realmente asigne algo al objeto de sesión, la ID de sesión parece seguir cambiando en cada solicitud.

Me encontré con esto con un software de prueba dividida que escribo.


1
2018-01-27 00:35



Aquí hay un método que es un poco más simple que la respuesta aceptada.

public static class SessionManager
    {
        private static List<User> _sessions = new List<User>();

        public static void RegisterLogin(User user)
        {
            if (user != null)
            {
                _sessions.RemoveAll(u => u.UserName == user.UserName);
                _sessions.Add(user);
            }
        }

        public static void DeregisterLogin(User user)
        {
            if (user != null)
                _sessions.RemoveAll(u => u.UserName == user.UserName && u.SessionId == user.SessionId);
        }

        public static bool ValidateCurrentLogin(User user)
        {
            return user != null && _sessions.Any(u => u.UserName == user.UserName && u.SessionId == user.SessionId);
        }
    }

    public class User {
        public string UserName { get; set; }
        public string SessionId { get; set; }
    }

Con esto, durante su proceso de inicio de sesión después de haber verificado al usuario, crea una instancia de la clase User y le asigna el nombre de usuario y la identificación de la sesión, la guarda como un objeto Session y luego llama a la función RegisterLogin.

Luego, en cada carga de página, obtiene el objeto de sesión y lo pasa a la función ValidateCurrentLogin.

La función DeregisterLogin no es estrictamente necesaria, pero mantiene el objeto _sessions lo más pequeño posible.


0
2018-02-27 19:12