Je développe une application qui doit empêcher la connexion multiple en utilisant le même nom d'utilisateur et le même mot de passe.
Si cela se produit sur le même ordinateur, il est évident que nous devons utiliser quelque chose avec la session utilisateur, mais cela devrait également empêcher si les utilisateurs se connectent sur des ordinateurs différents en utilisant le même nom d'utilisateur et le même mot de passe.
Nous devons garder les choses à l’esprit:
J'apprécierais toute aide sur ceci.
Si l'utilisateur ferme le navigateur sans se déconnecter.
En particulier, ce cas est difficile et non fiable à détecter. Vous pouvez utiliser l'événement beforeunload
en Javascript, mais vous dépendez entièrement de l'activation de JS dans le navigateur et de la prise en charge de cet événement non standard par le navigateur (par exemple, Opera ne le fait pas). C'est également l'une des principales raisons pour lesquelles je suggérerais de simplement déconnecter l'utilisateur précédemment connecté au lieu d'empêcher la connexion. C’est aussi plus convivial et sécurisé pour le cas où l’utilisateur a "oublié" de se déconnecter de l’autre ordinateur.
Le moyen le plus simple est de laisser la variable User
une variable static Map<User, HttpSession>
et de la mettre en œuvre HttpSessionBindingListener
(et Object#equals()
et Object#hashCode()
).
public class User implements HttpSessionBindingListener {
// All logins.
private static Map<User, HttpSession> logins = new HashMap<User, HttpSession>();
// Normal properties.
private Long id;
private String username;
// Etc.. Of course with public getters+setters.
@Override
public boolean equals(Object other) {
return (other instanceof User) && (id != null) ? id.equals(((User) other).id) : (other == this);
}
@Override
public int hashCode() {
return (id != null) ? (this.getClass().hashCode() + id.hashCode()) : super.hashCode();
}
@Override
public void valueBound(HttpSessionBindingEvent event) {
HttpSession session = logins.remove(this);
if (session != null) {
session.invalidate();
}
logins.put(this, event.getSession());
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
logins.remove(this);
}
}
Lorsque vous vous connectez à la User
comme suit:
User user = userDAO.find(username, password);
if (user != null) {
request.getSession.setAttribute("user", user);
} else {
// Show error.
}
alors il invoquera la valueBound()
qui supprimera tout utilisateur précédemment connecté de la carte logins
et invalidera la session.
Lorsque vous vous déconnectez de la User
comme suit:
request.getSession().removeAttribute("user");
ou lorsque la session a expiré, la fonction valueUnbound()
est invoquée, ce qui supprime l'utilisateur de la carte logins
.
Créez une table dans votre base de données - appelons-la [online_users]
- avec trois champs:
[online_users]
1. username
2. login_time
3. logout_time
Chaque fois qu'un utilisateur se connecte, insérez son nom et l'heure de connexion dans [online_users]
.
Sur toutes les pages nécessitant une connexion des utilisateurs, placez cette condition: cochez [online_users]
pour voir si le logout_time
de l'utilisateur est vide ou non.
Lorsqu'un utilisateur appuie sur un bouton de déconnexion, définissez le logout_time
dans [online_users]
pour le nom de cet utilisateur.
Si quelqu'un tente de se connecter avec un nom d'utilisateur et un mot de passe actifs, recherchez username
et logout_time
et affichez un message indiquant que l'utilisateur est déjà connecté. Et surtout, définissez logout_time
sur MULTIPLELOGIN
pour cet utilisateur.
Si cet utilisateur est connecté à une autre machine, s'il se met à jour ou navigue vers une autre page, le site lui dira qu'il a été déconnecté. Ensuite, l'utilisateur peut être redirigé vers la page d'accueil du site.
Prenez un champ supplémentaire dans la table avec le nom de colonne et indiquez «IsLoggedIn» comme champ de bits et définissez-le sur true jusqu'à ce que l'utilisateur soit connecté. Dès que l'utilisateur se déconnecte, définissez-le sur false. Ceci doit être effectué pour les délais d'expiration de session également. Dès que la session expire, ce champ doit être défini sur false automatiquement à l'aide de triggers ou thru SP call
bonne solution est toujours la bienvenue
Je suggérerais simplement d'utiliser un cadre de sécurité pour gérer tous ces détails pour vous. Spring Security , par exemple, est assez facile à intégrer dans un projet existant, peut être personnalisé si nécessaire - et plus important encore, il prend en charge de manière intégrée la détection et le contrôle des connexions simultanées .
Ne réinventez pas la roue quand vous n'en avez pas besoin, sinon vous passerez un bon bout de temps à créer une roue cahoteuse.
Je conseillerais également pour la solution de Shantanu Gupta - avoir une colonne de base de données indiquant que l'utilisateur est actuellement connecté et mettre à jour cette colonne en conséquence.
Pour "capturer" l'expiration de la session, vous devez définir dans votre web.xml
:
<listener>
<listener-class>com.foo.MySessionListener</listener-class>
</listener>
Où MySessionListener
est votre implémentation de l'interface HttpSessionListener
(fournie par l'API Servlet).
Peut-être trop simplifié, mais bon… ça marche pour moi dans Web2Py:
Seulement en cas de connexion réussie, j'écris le SessionID (response.session_id) dans la table auth_membership. Sur la page d'arrivée (page d'index), je vérifie si le response.session_id actuel est égal au SessionID provenant de la base de données. Si tel est le cas, tout va bien. Sinon, - (le "plus ancien", premier) utilisateur est poliment déconnecté.
Ce qui précède fonctionne car, à chaque connexion, un NEW response.session_id est créé et stocké dans la base de données. La vérification est effectuée uniquement sur la page de destination (qui, dans mon application, est la plus importante et lance de nombreuses autres fonctions). , donc pas trop de hits de base de données pour ce qui précède. Ce qui précède ne dépend pas de la déconnexion de l'utilisateur. Aucune adresse IP impliquée (que d'autres ont mentionnée, souffre de ses propres problèmes) Il ne permet qu'à UN SEUL utilisateur d'être connecté à la fois et il déconnecte l'utilisateur "plus ancien".
J'espère que ça aide NeoToren
J'ai mis en place une solution possible pour moi,
dans le loginFilter que j'utilise, je définis un lastloggedin, un userloggedin et un userSession dans l'enregistrement utilisateur de mon système.
user.setUser_lastlogged(new Date());
user.setUser_loggedin(true);
user.setSessionId(request.getSession().getId());
appService.saveUsers(user);
donc, quand je vais à l'une de mes actions struts2, j'ai un bout de code dans la méthode prepare.
@Override
public void prepare() throws Exception {
UsersBase usercheck = appservice.getUserByUsername((String)request.getSession().getAttribute("j_username"));
if(request.getSession().getId().equals(usercheck.getSessionId())){
request.getSession().invalidate();
}
}
Cela déconnectera l'utilisateur quand il se connectera sur une autre machine, ou si vous ne voulez pas vous connecter, je pourrais faire ce qui suit sur le loginFilter
UsersBase userdto = appService.getUserByUsername(username);
if (userdto != null) {
if ((userdto.getUser_loggedin())) {
if (request.getSession().getId().equals(userdto.getSessionId())) {
authRequest.eraseCredentials();
request.getSession().setAttribute("error", "You are already logged in ");
}
}
}
Cela peut être facilement appliqué si vous avez une session. Pour chaque connexion de navigateur, vous devez créer un enregistrement de session dans la base de données de session. L'ID de session peut être utilisé comme cookie d'authentification. La base de données de session a également un index avec nom d'utilisateur. Lors de la connexion, vous pouvez interroger la base de données pour vérifier le nombre de sessions. Nous permet en fait une session pour chaque type. Par exemple, l'utilisateur peut avoir une connexion depuis un téléphone mobile et une autre depuis un navigateur. Mais il ne peut pas y avoir 2 sessions de navigateur.
Pour résoudre le problème que vous avez mentionné. Vous avez 2 options,
Disposez d'un délai d'attente de session très court (environ 5 minutes) et prolongez la session à chaque utilisation. De cette façon, l'utilisateur sera automatiquement déconnecté s'il quitte sans se déconnecter.
Bump l'autre session. La nouvelle session remplace l'ancienne session. La session interrompue reste dans la base de données avec un indicateur spécial pendant 24 heures. Nous affichons un message pour indiquer à l’utilisateur que l’autre session est en train d’être modifiée et affiche l’heure et l’adresse IP De cette façon, l'utilisateur sera averti si son compte est compromis.
Je suivais la dernière adresse IP connue de chaque utilisateur et un horodatage pour la dernière utilisation de cette adresse IP. Ensuite, vous pouvez simplement bloquer l'accès à partir d'autres adresses IP pendant 5 minutes, une heure ou ce que vous préférez.
Chaque fois que l'adresse IP change, vous pouvez a) faire expirer l'ancienne session de l'utilisateur pour le forcer à se reconnecter et b) incrémenter un compteur par utilisateur (pouvant être mis à zéro toutes les heures). Si le compteur dépasse 5 (ou quelque chose), vous pouvez bloquer tout accès au compte de l'utilisateur pendant une période plus longue.
Vous pouvez stocker une sorte d'identifiant de session pour l'utilisateur lors de la connexion. Lorsque l'utilisateur se déconnecte ou lorsque la session expire, vous supprimez à nouveau ces informations.
Lorsqu'un utilisateur essaie de se connecter et que vous avez déjà un identifiant de session stocké pour cet utilisateur, laissez-le confirmer, puis annulez l'ancienne session.
Un utilisateur voudra certainement se reconnecter immédiatement si le navigateur est tombé en panne ou quelque chose du genre. Le laisser attendre que la session expire risque donc d'être ennuyeux.
Est-ce que cela a du sens pour votre application?
Utilisez le jeton
Lorsque l'utilisateur se connecte avec succès, le côté serveur renvoie une chaîne de jeton côté client/navigateur et le côté serveur enregistre une mappe avec ID utilisateur - jeton. pas pareil, cet utilisateur enregistre plusieurs fois.
Lors de la fermeture de session, il enregistre le jeton dans les cookies ou le système de fichiers côté client, et l'apporte lors de la prochaine connexion.
Table:
userid:token:log_date