2009-12-04 8 views
6

Nuestra aplicación Rails está utilizando Restful Authentication para la administración de usuarios/sesiones y parece que el inicio de sesión en la misma cuenta desde varias computadoras mata la sesión en las otras computadoras, lo que elimina la función "Recordarme".Autenticación reposada: ¿permite inicios de sesión de varias computadoras?

Decir que estoy en casa e iniciar sesión en la aplicación (y marcar "Recordarme"). Luego voy a la oficina e inicio sesión (y también marque "Recordarme"). Luego, cuando regreso a casa, vuelvo a la aplicación y tengo que volver a iniciar sesión.

¿Cómo puedo permitir el inicio de sesión desde varias máquinas y mantener la funcionalidad "Recordarme" funcionando en todas ellas?

Respuesta

9

Vas a sacrificar algo de seguridad al hacer esto, pero definitivamente es posible. Hay dos formas en que deberías poder lograr esto.

En la primera, puede anular el método make_token en su modelo de usuario. El modelo se implementa actualmente de la siguiente manera.

def make_token 
    secure_digest(Time.now, (1..10).map{ rand.to_s }) 
end 

Cada vez que un usuario inicia una sesión en, con o sin una cookie, el método de make_token se llama que genera y guarda un nuevo remember_token para el usuario. Si tenía algún otro valor que fuera exclusivo del usuario y no se pudiera adivinar, puede reemplazar el método make_token.

def make_token 
    secure_digest(self.some_secret_constant_value) 
end 

Esto garantizaría que el testigo nunca cambia, sino que también permitirá a cualquier persona que tiene el token para suplantar al usuario.

Aparte de esto, si echa un vistazo al método handle_remember_cookie! en el archivo authenticated_system.rb, debería poder cambiar este método para que funcione.

def handle_remember_cookie!(new_cookie_flag) 
    return unless @current_<%= file_name %> 
    case 
    when valid_remember_cookie? then @current_<%= file_name %>.refresh_token # keeping same expiry date 
    when new_cookie_flag  then @current_<%= file_name %>.remember_me 
    else        @current_<%= file_name %>.forget_me 
    end 
    send_remember_cookie! 
end 

Se dará cuenta de que este método llama tres métodos en el modelo de usuario, refresh_token, remember_me y forget_me.

def remember_me 
    remember_me_for 2.weeks 
    end 

    def remember_me_for(time) 
    remember_me_until time.from_now.utc 
    end 

    def remember_me_until(time) 
    self.remember_token_expires_at = time 
    self.remember_token   = self.class.make_token 
    save(false) 
    end 

    # 
    # Deletes the server-side record of the authentication token. The 
    # client-side (browser cookie) and server-side (this remember_token) must 
    # always be deleted together. 
    # 
    def forget_me 
    self.remember_token_expires_at = nil 
    self.remember_token   = nil 
    save(false) 
    end 

    # refresh token (keeping same expires_at) if it exists 
    def refresh_token 
    if remember_token? 
     self.remember_token = self.class.make_token 
     save(false)  
    end 
    end 

Los tres métodos restablecen el token. forget_me lo establece en nil, mientras que los otros dos lo establecen en el valor devuelto por make_token. Puede anular estos métodos en el modelo de usuario para evitar que restablezcan el token si existe y no ha caducado. Ese es probablemente el mejor enfoque, o podría agregar algo de lógica adicional al método handle_remember_cookie!, aunque eso probablemente sería más trabajo.

Si yo fuera usted, anularía remember_me_until, forget_me y refresh_token en el modelo de usuario. Lo siguiente debería funcionar

def remember_me_until(time) 
    if remember_token? 
    # a token already exists and isn't expired, so don't bother resetting it 
    true 
    else 
    self.remember_token_expires_at = time 
    self.remember_token   = self.class.make_token 
    save(false) 
    end 
end 

# 
# Deletes the server-side record of the authentication token. The 
# client-side (browser cookie) and server-side (this remember_token) must 
# always be deleted together. 
# 
def forget_me 
    # another computer may be using the token, so don't throw it out 
    true 
end 

# refresh token (keeping same expires_at) if it exists 
def refresh_token 
    if remember_token? 
    # don't change the token, so there is nothing to save 
    true  
    end 
end 

Tenga en cuenta que al hacer esto, está eliminando las funciones que lo protegen del robo de testigos. Pero esa es una decisión de costo-beneficio que puede tomar.

+0

¡Muchas gracias! Entonces, ¿cómo funciona la funcionalidad de un usuario marcando "Recordarme" ahora? ¿Todavía los recuerda por el tiempo establecido en el método 'remember_me'? – Shpigford

+0

Todavía los recuerda durante 2 semanas, como en el método remember_me, pero esas 2 semanas comienzan la primera vez que se usa el token. En otras palabras, si inicia sesión desde la computadora A, luego 10 días inicie sesión desde la computadora B, 4 días después, la ficha caduca en ambas computadoras. – jcnnghm

+0

Genial. ¡De nuevo, gracias por tu ayuda! – Shpigford

0

Puede cambiar lo que remember_token es para lograr esto. Se puede establecer que:

self.remember_token = encrypt("#{email}--extrajunkcharsforencryption") 

en lugar de

self.remember_token = encrypt("#{email}--#{remember_token_expires_at}") 

Ahora hay equipo o nada de tiempo específico acerca de la ficha y que pueda mantenerse conectado desde múltiples máquinas.

+0

Hmmm, ¿a qué versión de Restful Authentication se refiere? Estoy usando una versión bastante reciente y 'remember_token' se establece con una serie de métodos mucho más complejos y el cifrado SHA1. – Shpigford

+1

Ah, lo siento. Esta es una versión bastante antigua que he tenido funcionando durante al menos un año. No me di cuenta de que había cambiado tanto. – erik

Cuestiones relacionadas