Devise + Angular + Rails 4:更改登录用户未验证用户名/密码

时间:2014-06-03 04:32:27

标签: ruby-on-rails angularjs devise

......接近期望的行为!

有一个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)

感谢任何帮助!

1 个答案:

答案 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寻找一个很好的写作。

基于令牌的身份验证的基础是:

  • Angular将用户名和密码发送到服务器
  • 服务器验证用户名和密码,生成一个&#39;令牌&#39;对于该用户并将令牌发送回角度
  • Angular存储该令牌(在本地存储/ cookie中)
  • 对于每个新请求(例如:/api/private_thing.json),Angular会发送&#39;令牌&#39; (在标题或参数中)
  • 服务器检查该令牌是否属于具有查看&#39; private_thing&#39;
  • 权限的用户记录
祝你好运

更新

如果您打算使用选项2,请将创建操作更改为:

def create 
  sign_out if is_any_user
  render :status => 200, json: { success: true, info: "Logged in", user: current_user }
end