feat: add remember me functionality to login
- Implement remember me checkbox in login form - Update sessions controller to handle remember me logic - Enhance session management to prevent session hijacking - Add tests for remember me functionality This commit introduces a "Remember me" feature that allows users to stay logged in across sessions. It includes updates to the login form, session handling in the controller, and additional tests to ensure the functionality works as expected. The changes also improve security by validating session tokens to prevent session hijacking.
This commit is contained in:
parent
f110f26c0b
commit
63cebef027
@ -174,3 +174,16 @@ input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
span {
|
||||||
|
margin-left: 20px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#session_remember_me {
|
||||||
|
width: auto;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@ class SessionsController < ApplicationController
|
|||||||
# if user && user.authenticate(params[:session][:password])
|
# if user && user.authenticate(params[:session][:password])
|
||||||
if user&.authenticate(params[:session][:password])
|
if user&.authenticate(params[:session][:password])
|
||||||
reset_session
|
reset_session
|
||||||
remember user
|
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
|
||||||
log_in user
|
log_in user
|
||||||
redirect_to user
|
redirect_to user
|
||||||
else
|
else
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
module SessionsHelper
|
module SessionsHelper
|
||||||
def log_in(user)
|
def log_in(user)
|
||||||
session[:user_id] = user.id
|
session[:user_id] = user.id
|
||||||
|
# 防范会话重放攻击
|
||||||
|
session[:session_token] = user.session_token
|
||||||
end
|
end
|
||||||
|
|
||||||
def remember(user)
|
def remember(user)
|
||||||
@ -11,7 +13,10 @@ module SessionsHelper
|
|||||||
|
|
||||||
def current_user
|
def current_user
|
||||||
if (user_id = session[:user_id])
|
if (user_id = session[:user_id])
|
||||||
@current_user ||= User.find_by(id: user_id)
|
user = User.find_by(id: user_id)
|
||||||
|
if user && session[:session_token] == user.session_token
|
||||||
|
@current_user = user
|
||||||
|
end
|
||||||
elsif (user_id = cookies.encrypted[:user_id])
|
elsif (user_id = cookies.encrypted[:user_id])
|
||||||
user = User.find_by(id: user_id)
|
user = User.find_by(id: user_id)
|
||||||
if user && user.authenticated?(cookies[:remember_token])
|
if user && user.authenticated?(cookies[:remember_token])
|
||||||
|
@ -23,6 +23,13 @@ class User < ApplicationRecord
|
|||||||
def remember
|
def remember
|
||||||
self.remember_token = User.new_token
|
self.remember_token = User.new_token
|
||||||
update_attribute(:remember_digest, User.digest(remember_token))
|
update_attribute(:remember_digest, User.digest(remember_token))
|
||||||
|
remember_digest
|
||||||
|
end
|
||||||
|
|
||||||
|
# 返回一个会话令牌,防止会话劫持
|
||||||
|
# 简单起见,直接使用记忆令牌
|
||||||
|
def session_token
|
||||||
|
remember_digest || remember
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
@ -10,6 +10,11 @@
|
|||||||
<%= f.label :password %>
|
<%= f.label :password %>
|
||||||
<%= f.password_field :password, class: 'form-control' %>
|
<%= f.password_field :password, class: 'form-control' %>
|
||||||
|
|
||||||
|
<%= f.label :remember_me, class: "checkbox inline" do %>
|
||||||
|
<%= f.check_box :remember_me %>
|
||||||
|
<span>Remember me on this computer</span>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= f.submit "Log in", class: "btn btn-primary" %>
|
<%= f.submit "Log in", class: "btn btn-primary" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
19
test/helpers/sessions_helper_test.rb
Normal file
19
test/helpers/sessions_helper_test.rb
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class SessionsHelperTest < ActionView::TestCase
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@user = users(:michael)
|
||||||
|
remember(@user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "current_user returns right user when session is nil" do
|
||||||
|
assert_equal @user, current_user
|
||||||
|
assert is_logged_in?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "current_user returns nil when remember digest is wrong" do
|
||||||
|
@user.update_attribute(:remember_digest, User.digest(User.new_token))
|
||||||
|
assert_nil current_user
|
||||||
|
end
|
||||||
|
end
|
@ -50,4 +50,15 @@ class UsersLoginTest < ActionDispatch::IntegrationTest
|
|||||||
assert_select "a[href=?]", logout_path, count: 0
|
assert_select "a[href=?]", logout_path, count: 0
|
||||||
assert_select "a[href=?]", user_path(@user), count: 0
|
assert_select "a[href=?]", user_path(@user), count: 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "login with remembering" do
|
||||||
|
log_in_as(@user, remember_me: '1')
|
||||||
|
assert_not cookies[:remember_token].blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "login without remembering" do
|
||||||
|
log_in_as(@user, remember_me: '1')
|
||||||
|
log_in_as(@user, remember_me: '0')
|
||||||
|
assert cookies[:remember_token].blank?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,6 +17,16 @@ module ActiveSupport
|
|||||||
!session[:user_id].nil?
|
!session[:user_id].nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add more helper methods to be used by all tests here...
|
# def log_in_as(user)
|
||||||
|
# session[:user_id] = user.id
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ActionDispatch::IntegrationTest
|
||||||
|
def log_in_as(user, password: 'password', remember_me: '1')
|
||||||
|
post login_path, params: { session: { email: user.email,
|
||||||
|
password: password,
|
||||||
|
remember_me: remember_me } }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user