Gem 怎么在 Grape 中集成 Devise 的验证?

qichunren · 发布于 2012年01月09日 · 最后由 a805717453 回复于 2014年03月27日 · 6953 次阅读
F46600

我想给ruby-china做一个iOS客户端。我是使用了intridea做的grape https://github.com/intridea/grape 这个gem来构建API支持的,由于ruby-china是使用了devise 1.5.2 https://github.com/plataformatec/devise 这个gem来做用户注册和登录验证的,感觉它封装很厉害,或者我没有搞懂,我都动都动不了啊。

现在我怎么样将devise的验证支持加到api中去呢?

我了解到Devise对 http header basic auth和 token_authentication_key 都是支持的,介但是ruby-china的devise配置文件中是没有开启的,虽然代码注释中是打开的。

http://ruby-china.org/topics/612

这是我提交的一点代码,对其中的空的方法寻求帮助。

https://github.com/qichunren/ruby-china/commit/b7bb58b7f7d6191aac922b3c80fd8b1b494e9f24

这是测试验证的代码:

require "rubygems"
require 'active_support'
require 'net/http'

Net::HTTP.start('localhost', 3001) {|http|

  basic =  { "HTTP_AUTHORIZATION" => "Basic #{ActiveSupport::Base64.encode64("your_login_id:your_password")}" }

  req = Net::HTTP::Get.new('/api/v1/users/me')
  #req.basic_auth 'login_id', 'password'
  response = http.request(req)
  print response.body
}
共收到 15 条回复
96

我的想法是像 rubygems github 的api那样,写个filter来判断 http header 中有没 Authorization ,如果有 User.find_by_token_authentication_key ,如果match 就 赋值current_user,大概意思是这样

96

config.ru

require "grape"
require "pry"
class API < Grape::API
  version 'v1', :using => :header, :format => :json
  helpers do
    def current_user
      #binding.pry
      @current_user ||= (env["HTTP_AUTHORIZATION"] == "xxx" ? "vkill" : nil)
    end
    def authenticate!
      error!('401 Unauthorized', 401) unless current_user
    end
  end
  resource :account do
    get '/private' do
      authenticate!
      @current_user
    end
  end
end

run API

curl test

➜  ~  curl -H 'Authorization:xxx'  http://127.0.0.1:9292/v1/account/private
"vkill"#
➜  ~  curl -H 'Authorization:yyy'  http://127.0.0.1:9292/v1/account/private
401 Unauthorized#
366

我是用devise的 single access token, 做 xAuth

devise  :token_authenticatable
config.token_authentication_key = :single_access_token

然后拿客户端传入的 env['HTTP_X_USER_ACCESS_TOKEN'] 和 devise的 authentication_token 做比较

F46600

#3楼 @allenwei 你可以再说详细一点吗?

F46600

#2楼 @vkill 你的代码我看懂了,就是一个key来判断登录的,现在怎么样与ruby-china的devise集成呢?这是问题的关键啊。

96

#5楼 @qichunren 你写的api不是 mount 到 ruby-china 吗?如果是 mount 到 ruby-china 的话,那是完全脱离 ruby-china 原来的后台的,那么也就是说和原来的 devise 没一点点关系,你只用 User 里的 token_authentication_key 这个字段就是了,不知道我说清楚没有呢?

366
  1. 加一个devise的module token_authenticatable
  2. 加一个before filter到user model, before_save :ensure_authentication_token, 这个是devise提供的 3.在devise的initializer里面加入 config.token_authentication_key = :single_access_token
  3. 定义一些helper
helpers do
  def warden
    env['warden'].params[:single_access_token] = env["HTTP_X_USER_ACCESS_TOKEN"]
    env['warden']
  end

  def current_user
    @current_user ||= env["HTTP_X_USER_ACCESS_TOKEN"].nil? ? nil : User.find_by_authentication_token(env["HTTP_X_USER_ACCESS_TOKEN"])
  end

  def authenticate!
    unless current_user
      logger.debug "authenticate fail with HTTP_X_USER_ACCESS_TOKEN #{env['HTTP_X_USER_ACCESS_TOKEN']} "
      raise_401
    end
  end
end
  1. 定义一个api, 让用户得到token post "/login" do warden.logout if user = warden.authenticate(:scope => :user) user.ensure_authentication_token! {:user => {:display_name => user.display_name, :single_access_token => user.authentication_token, :updated_at => user.updated_at, :id => user.id }} else raise_401 end end
F46600

#7楼 @allenwei 非常感谢你的帮助,现在可以使用single_access_token得到用前登录用户访问授权的api了,但是,你说的定义一个api,让用户得到token这一步我还是不能搞定,我是这样做的: curl -d "user[login]=mylogin&user[password]=mypassword" http://localhost:3000/api/v1/login
返回结果是执行post "/login"中的else. 验证不成功,得不到authentication_token. 我能肯定我输入的用户名和密码是正确的。

warden.authenticate(:scope => :user)方法我看源代码,没有看明白

366

#8楼 @qichunren 不知道具体的问题是什么, 我也是看了半天源码研究出来的, 你可以再看看warden的代码 `

366

#8楼 @qichunren 在 warden.logout 后面加一行 warden.params[:controller] = "sessions" 试试

F46600

试了,还是搞不出来,授权验证通不过,取authentication_token这一步放到rails app中去搞算了,其它的api放在grape中来实现.

366

#11楼 @qichunren 没帮到你,最近比较忙,改天有时间,我写个demo出来

1153

@allenwei 最近有时间写一个关于这个的demo吗?呵呵

1153

@qichunren devise和Grape集成的问题,解决了吗?呵呵~~偶也遇到了。

96

解决方法如下

post "/login" do
  request.env['devise.allow_params_authentication'] = true
  if user = warden.authenticate(:scope => :user)
    user.ensure_authentication_token!
    {:user => {:display_name => user.display_name, :single_access_token => user.authentication_token, :updated_at => user.updated_at, :id => user.id }}
  else
    raise_401
  end
end
16楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册