Devise limite une session par utilisateur à la fois

Mon application utilise Rails 3.0.4 et Devise 1.1.7.

Je cherche un moyen d’empêcher les utilisateurs de partager des comptes car l’application est un service basé sur un abonnement. Je recherche depuis plus d’une semaine et je ne sais toujours pas comment implémenter une solution. J’espère que quelqu’un a mis en place une solution et peut me diriger dans la bonne direction.

Solution (Merci à tous pour vos réponses et votre perspicacité!)

Dans application controller.rb

before_filter :check_concurrent_session def check_concurrent_session if is_already_logged_in? sign_out_and_redirect(current_user) end end def is_already_logged_in? current_user && !(session[:token] == current_user.login_token) end 

Dans session_controller qui remplace le contrôleur Devise Sessions:

 skip_before_filter :check_concurrent_session def create super set_login_token end private def set_login_token token = Devise.friendly_token session[:token] = token current_user.login_token = token current_user.save end 

En migration AddLoginTokenToUsers

 def self.up change_table "users" do |t| t.ssortingng "login_token" end end def self.down change_table "users" do |t| t.remove "login_token" end end 

Vous ne pouvez pas le faire.

  • Vous pouvez contrôler les adresses IP des utilisateurs afin d’empêcher la présence d’utilisateurs de deux adresses IP à la fois. Et vous pouvez lier un identifiant et une adresse IP. Vous pouvez essayer de vérifier les villes et autres données de géolocalisation via IP pour bloquer l’utilisateur.
  • Vous pouvez définir des cookies pour contrôler autre chose.

Mais rien de cela ne garantit qu’un seul utilisateur utilise cette connexion, et que ces 105 IP du monde entier n’appartiennent pas à un seul utilisateur unique, qui utilise le proxy ou autre.

Et le dernier: vous n’en avez jamais besoin sur Internet.

UPD

Cependant, ce que je demande, c’est d’empêcher plusieurs utilisateurs d’utiliser le même compte simultanément, ce qui, selon moi, devrait être possible

Vous pouvez donc stocker des jetons contenant des données chiffrées: IP + chaîne secrète + agent utilisateur + version du navigateur utilisateur + SE utilisateur + toute autre information personnelle: encrypt(IP + "some secret ssortingng" + request.user_agent + ...) . Et puis vous pouvez définir une session ou un cookie avec ce jeton. Et à chaque demande, vous pouvez le récupérer: si l’utilisateur est le même? Est-ce qu’il utilise le même navigateur et la même version de navigateur du même système d’exploitation, etc.

Vous pouvez également utiliser des jetons dynamics: vous changez de jeton à chaque demande, de sorte qu’un seul utilisateur peut utiliser le système par session, car chaque jeton de demande sera modifié, un autre utilisateur sera déconnecté pour autant que son jeton ait expiré.

Ce bijou fonctionne bien: https://github.com/phatworx/devise_security_extension

Ajouter à Gemfile

 gem 'devise_security_extension' 

après installation du paquet

 rails g devise_security_extension:install 

Puis courir

 rails g migration AddSessionLimitableToUsers unique_session_id 

Modifier le fichier de migration

 class AddSessionLimitableToUsers < ActiveRecord::Migration def change add_column :users, :unique_session_id, :string, limit: 20 end end 

Puis courir

 rake db:migrate 

Modifier votre fichier app / models / user.rb

 class User < ActiveRecord::Base devise :session_limitable # other devise options ... rest of file ... end 

Terminé. La connexion à partir d'un autre navigateur entraînera la mort de toutes les sessions précédentes. Le gem actual notifie à l'utilisateur qu'il est sur le sharepoint tuer une session en cours avant de se connecter.

En ce qui concerne son implémentation dans Devise, ajoutez-la à votre modèle User.rb. Quelque chose comme cela les déconnectera automatiquement (non testé).

  def token_valid? # Use fl00rs method of setting the token session[:token] == cookies[:token] end ## Monkey Patch Devise methods ## def active_for_authentication? super && token_valid? end def inactive_message token_valid? ? super : "You are sharing your account." end 

C’est ainsi que j’ai résolu le problème de la session en double.

routes.rb

  devise_for :users, :controllers => { :sessions => "my_sessions" } 

contrôleur my_sessions

 class MySessionsController < Devise::SessionsController skip_before_filter :check_concurrent_session def create super set_login_token end private def set_login_token token = Devise.friendly_token session[:token] = token current_user.login_token = token current_user.save(validate: false) end end 

application_controller

  def check_concurrent_session if duplicate_session? sign_out_and_redirect(current_user) flash[:notice] = "Duplicate Login Detected" end end def duplicate_session? user_signed_in? && (current_user.login_token != session[:token]) end 

Modèle utilisateur Ajouter un champ de chaîne via une migration nommée login_token

Cela remplace le contrôleur de session Devise par défaut mais en hérite également. Lors d'une nouvelle session, un jeton de session de connexion est créé et stocké dans login_token sur le modèle User. Dans le contrôleur d'application, nous appelons check_concurrent_session qui se déconnecte et redirige l'utilisateur_actuel après avoir appelé la check_concurrent_session duplicate_session? fonction.

Ce n'est pas la façon la plus propre de s'y prendre, mais ça marche vraiment.

J’ai trouvé que la solution dans la publication originale ne fonctionnait pas bien pour moi. Je voulais que le premier utilisateur soit déconnecté et qu’une page de connexion soit présentée. De plus, la sign_out_and_redirect(current_user) ne semble pas fonctionner comme prévu. En utilisant la substitution SessionsController dans cette solution, je l’ai modifiée pour utiliser les websockets comme suit:

 def create super force_logout end private def force_logout logout_subscribe_address = "signout_subscribe_response_#{current_user[:id]}" logout_subscribe_resp = {:message => "#{logout_subscribe_address }: #{current_user[:email]} signed out."} WebsocketRails[:signout_subscribe].sortinggger(signout_subscribe_address, signout_subscribe_resp) end end 

Assurez-vous que toutes les pages Web sont abonnées au canal de déconnexion et associez-le à la même action logout_subscribe_address . Dans mon application, chaque page possède également un bouton de «déconnexion», qui déconnecte le client via la session de conception Destroy action. Lorsque la réponse WebSocket est déclenchée dans la page Web, il suffit de cliquer sur ce bouton – la logique de déconnexion est appelée et la page de connexion s’affiche pour le premier utilisateur.

Cette solution ne nécessite pas non plus le skip_before_filter :check_concurrent_session et le modèle login_token car il déclenche la déconnexion forcée sans préjudice.

Pour l’enregistrement, le devise_security_extension semble fournir les fonctionnalités devise_security_extension . Il met également en place une alerte appropriée avertissant le premier utilisateur de ce qui s’est passé (je n’ai pas encore trouvé comment le faire).

Gardez une trace des adresses IP uniques utilisées par utilisateur. De temps en temps, exécutez une parsing sur ces adresses IP – le partage serait évident si un seul compte avait des connexions simultanées provenant de différents fournisseurs de services Internet dans différents pays. Notez que le simple fait d’avoir une adresse IP différente n’est pas une raison suffisante pour la considérer comme partagée – certains fournisseurs d’access utilisent des proxys à tour de rôle, de sorte que chaque hit serait nécessairement une adresse IP différente.

Bien que vous ne puissiez pas empêcher de manière fiable les utilisateurs de partager un compte, vous pouvez (je pense) empêcher que plusieurs utilisateurs soient connectés simultanément au même compte. Vous ne savez pas si cela est suffisant pour votre modèle d’entreprise, mais vous éviterez bien des problèmes abordés dans les autres réponses. J’ai implémenté quelque chose qui est actuellement en version bêta et qui semble fonctionner raisonnablement bien – il y a quelques notes ici