......接近期望的行为!
有一个Devise / AngularJS / Rails 4项目,用户登录/注销工作几乎完美。发送回X-CSRF-Token并将用户的电子邮件以角度形式存储为cookie,以便用户点击刷新不会导致角度失去用户的知识。
我能看到的唯一问题是:如果您以管理员用户身份登录,那么没有注销 ...您只需使用其他一些有效凭据填写用户名/登录信息。 .Devise正在根据您的CSRF令牌对您进行身份验证,而不是实际验证该用户名和密码。
因此,您会认为您是以其他人身份登录的,但您实际上是以前登录的用户。我确定这很简单,但似乎无法指出它...需要强制设计/监护人始终在CREATE上检查用户名/密码 - 不要只是快乐使用csrf令牌。
这是我们的sessions_controller.rb:
class SessionsController < Devise::SessionsController
respond_to :json
def create
Rails.logger.debug("(SessionsController.create) ******* ")
user = warden.authenticate!(:scope => :user, :recall => "#{controller_path}#failure")
Rails.logger.debug("(SessionsController.create) back from warden.authenticate ")
render :status => 200,
:json => { :success => true,
:info => "Logged in",
:user => current_user
}
end
def destroy
warden.authenticate!(:scope => :user, :recall => "#{controller_path}#failure")
sign_out
render :status => 200,
:json => { :success => true,
:info => "Logged out",
}
end
def failure
render :status => 401,
:json => { :success => false,
:info => "Login Credentials Failed"
}
end
def show_current_user
warden.authenticate!(:scope => :user, :recall => "#{controller_path}#failure")
render :status => 200,
:json => { :success => true,
:info => "Current User",
:user => current_user
}
end
end
这是application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery #with: :exception
helper_method :require_user, :require_admin
after_filter :set_csrf_cookie_for_ng
def set_csrf_cookie_for_ng
Rails.logger.debug("set_csrf_cookie_for_ng called FAT:#{form_authenticity_token}")
Rails.logger.debug("protect_against_forgery = #{protect_against_forgery?}")
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
end
def require_user
Rails.logger.debug("require_user method in application controller")
if !user_signed_in?
flash[:notice] = "Please sign in!"
redirect_to new_user_session_path
end
end
def is_admin_user
return false if current_user.nil?
return current_user.admin?
end
def is_any_user
return false if current_user.nil?
return user_signed_in?
end
def verify_admin_user
unless is_admin_user
Rails.logger.debug("Unauthorized - Must be Admin")
respond_to do | format |
format.json { render :json => [], :status => :unauthorized }
end
end
end
def verify_any_user
unless user_signed_in?
Rails.logger.debug("Unauthorized - Must be Logged-in")
respond_to do | format |
format.json { render :json => [], :status => :unauthorized }
end
end
end
protected
def verified_request?
Rails.logger.debug("verified_request called f_a_t:#{form_authenticity_token}. X-XSRF-TOKEN:#{request.headers['X-XSRF-TOKEN']}")
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
end
routes.rb中:
devise_for :users, :controllers => {:sessions => "sessions"}
最后,在Angularjs方面,我们的httpprovider:
.config(["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
以下是我在日志中看到的如果您以管理员身份登录,然后您尝试以非管理员身份“登录”...请注意,“用户1”是管理员已登录。
Started POST "/users/sign_in.json" for 127.0.0.1 at 2014-06-02 23:21:25 -0500
Processing by SessionsController#create as JSON
Parameters: {"user"=>{"email"=>"nonadmin@example.com", "password"=>"[FILTERED]"}, "session"=>{"user"=>{"email"=>"nonadmin@example.com", "password"=>"[FILTERED]"}}}
verified_request called f_a_t:P2FZxg/qwooeNp0Ttme8urXoM9tzWh1bO5y28J8rTHc=. X-XSRF-TOKEN:P2FZxg/qwooeNp0Ttme8urXoM9tzWh1bO5y28J8rTHc=
(SessionsController.create) *******
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
(SessionsController.create) back from warden.authenticate
set_csrf_cookie_for_ng called FAT:P2FZxg/qwooeNp0Ttme8urXoM9tzWh1bO5y28J8rTHc=
protect_against_forgery = true
Completed 200 OK in 3ms (Views: 0.8ms | ActiveRecord: 0.5ms)
感谢任何帮助!
答案 0 :(得分:4)
Devise / warden(默认情况下)使用基于会话/ cookie的身份验证,因此它不是验证会话身份的CSRF令牌。
你有几个选择
1,当angular知道用户已登录时,禁用登录表单
2,在创建操作中明确签署用户(通过调用sign_out
)
3,(首选)不要在api上使用会话身份验证。 API应该是无状态的。我的意思是服务器不应该知道用户是否已登录&#39;。您应该为每个请求发送一个身份验证令牌(不依赖于会话)。在这里http://www.soryy.com/ruby/api/rails/authentication/2014/03/16/apis-with-devise.html寻找一个很好的写作。
基于令牌的身份验证的基础是:
更新
如果您打算使用选项2,请将创建操作更改为:
def create
sign_out if is_any_user
render :status => 200, json: { success: true, info: "Logged in", user: current_user }
end