2009-12-04 31 views
6

我们的Rails应用程序正在使用Restful Authentication进行用户/会话管理,似乎从多台计算机登录到同一个帐户会杀死其他计算机上的会话,从而导致“Remember me”功能失效。宁静的身份验证:允许从多台计算机登录?

所以说我在家里并登录到应用程序(并检查“记住我”)。然后我去办公室登录(并检查“记住我”)。然后,当我回到家时,我回到了应用程序,并且必须重新登录。

我该如何允许从多台机器登录并保持“记住我”的功能?

回答

9

这样做会牺牲一些安全性,但这绝对有可能。有两种方法可以实现这一点。

在第一个中,您可以覆盖用户模型中的make_token方法。该模型目前实施如下。

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

用户每次登录时,具有或不具有一个cookie,该make_token方法被调用,其生成并为用户节省了新remember_token。如果您还有其他一些对用户来说是独一无二的价值,那么您可以更换make_token方法。

def make_token 
    secure_digest(self.some_secret_constant_value) 
end 

这样可以确保令牌永不改变,但它也可以让任何获得令牌的人冒充用户。

除此之外,如果您查看authenticated_system.rb文件中的handle_remember_cookie!方法,则应该可以更改此方法以适合您。

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 

你会发现,这个方法调用用户模型中,refresh_tokenremember_meforget_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 

所有这三种方法都重置令牌。 forget_me将其设置为nil,而其他两个设置为由make_token返回的值。您可以在用户模型中重写这些方法,以防止它们在标记存在且未过期时重置该标记。这可能是最好的方法,或者你可以添加一些额外的逻辑到handle_remember_cookie!方法,虽然这可能会更多的工作。

如果我是你,我会在用户模型中覆盖remember_me_until,forget_merefresh_token。以下应该工作。

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 

请注意,通过这样做,您将取消防止令牌窃取的功能。但这是您可以制定的成本收益决定。

+0

谢谢! 那么,如何检查“记住我”的用户的功能现在工作?它是否还记得在'remember_me'方法中设置的时间量? – Shpigford 2009-12-11 03:20:52

+0

它仍然记得它们2周,就像在remember_me方法中一样,但是在第一次使用令牌时会开始2周。换句话说,如果您从计算机A登录,则从计算机B登录10天,4天后,这两个计算机上的令牌过期。 – jcnnghm 2009-12-11 15:04:41

+0

太好了。再次感谢你的帮助! – Shpigford 2009-12-11 18:25:11

0

您可以更改remember_token所要达到的效果。你可以将其设置为:的

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

代替

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

现在有具体的关于令牌没有计算机或时间,你可以在多台机器保持登录。

+0

嗯,你引用的是哪种版本的Restful Authentication?我正在使用一个非常新的版本,'remember_token'设置了更复杂的方法和SHA1加密。 – Shpigford 2009-12-04 18:22:27

+1

啊,对不起。这是我已经运行了至少一年的一个非常旧的版本。没有意识到它已经改变了很多。 – erik 2009-12-04 19:54:36