【发布时间】:2014-12-24 18:45:06
【问题描述】:
我很难理解,也很难在 API 中正确实施 用户身份验证。换句话说,我很难理解 Grape API 与 Backbone.js、AngularJS 或 Ember.js 等前端框架的集成。
我正在尝试调整所有不同的方法并阅读了很多关于此的内容,但谷歌返回给我的资源确实很糟糕,在我看来,好像没有关于这个主题的真正好的文章 - Rails 和用户身份验证使用设计和前端框架。
我将描述我目前的工作重点,希望您能就我的实施向我提供一些反馈,并可能为我指明正确的方向。
当前实施
我有后端 Rails REST API 和以下 Gemfile(我会故意缩短所有文件代码)
gem 'rails', '4.1.6'
gem 'mongoid', '~> 4.0.0'
gem 'devise'
gem 'grape'
gem 'rack-cors', :require => 'rack/cors'
我当前的实现只有具有以下路由的 API(routes.rb):
api_base /api API::Base
GET /:version/posts(.:format)
GET /:version/posts/:id(.:format)
POST /:version/posts(.:format)
DELETE /:version/posts/:id(.:format)
POST /:version/users/authenticate(.:format)
POST /:version/users/register(.:format)
DELETE /:version/users/logout(.:format)
我创建了以下模型 user.rb
class User
include Mongoid::Document
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
field :email, type: String, default: ""
field :encrypted_password, type: String, default: ""
field :authentication_token, type: String
before_save :ensure_authentication_token!
def ensure_authentication_token!
self.authentication_token ||= generate_authentication_token
end
private
def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless User.where(authentication_token: token).first
end
end
end
在我的控制器中,我创建了以下文件夹结构:controllers->api->v1,并且我创建了以下共享模块身份验证(authentication.rb强>)
module API
module V1
module Authentication
extend ActiveSupport::Concern
included do
before do
error!("401 Unauthorized", 401) unless authenticated?
end
helpers do
def warden
env['warden']
end
def authenticated?
return true if warden.authenticated?
params[:access_token] && @user = User.find_by(authentication_token: params[:access_token])
end
def current_user
warden.user || @user
end
end
end
end
end
end
所以每次当我想确保我的资源将被使用身份验证令牌调用时,我可以通过调用:include API::V1::Authentication 将其添加到葡萄资源:
module API
module V1
class Posts < Grape::API
include API::V1::Defaults
include API::V1::Authentication
现在我有另一个名为 Users(users.rb) 的 Grape 资源,在这里我实现了身份验证、注册和注销的方法。(我认为我在这里混合了苹果和梨,我应该将登录/注销过程提取到另一个葡萄资源——会话)。
module API
module V1
class Users < Grape::API
include API::V1::Defaults
resources :users do
desc "Authenticate user and return user object, access token"
params do
requires :email, :type => String, :desc => "User email"
requires :password, :type => String, :desc => "User password"
end
post 'authenticate' do
email = params[:email]
password = params[:password]
if email.nil? or password.nil?
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
return
end
user = User.find_by(email: email.downcase)
if user.nil?
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
return
end
if !user.valid_password?(password)
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
return
else
user.ensure_authentication_token!
user.save
status(201){status: 'ok', token: user.authentication_token }
end
end
desc "Register user and return user object, access token"
params do
requires :first_name, :type => String, :desc => "First Name"
requires :last_name, :type => String, :desc => "Last Name"
requires :email, :type => String, :desc => "Email"
requires :password, :type => String, :desc => "Password"
end
post 'register' do
user = User.new(
first_name: params[:first_name],
last_name: params[:last_name],
password: params[:password],
email: params[:email]
)
if user.valid?
user.save
return user
else
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
end
end
desc "Logout user and return user object, access token"
params do
requires :token, :type => String, :desc => "Authenticaiton Token"
end
delete 'logout' do
user = User.find_by(authentication_token: params[:token])
if !user.nil?
user.remove_authentication_token!
status(200)
{
status: 'ok',
token: user.authentication_token
}
else
error!({:error_code => 404, :error_message => "Invalid token."}, 401)
end
end
end
end
end
end
我意识到我在这里展示了大量代码,它可能没有意义,但这是我目前拥有的,我可以使用 authentication_token 来调用我的 API,这些 API 受模块 @987654329 保护@。
我觉得这个解决方案不好,但我真的在寻找更简单的方法如何通过 API 实现用户身份验证。我有几个问题,我在下面列出。
问题
- 您认为这种实现方式是否危险,如果是,为什么? - 我认为是,因为使用了一个令牌。有没有办法改善这种模式?我还看到了使用具有到期时间等的单独模型
Token的实现。但我认为这几乎就像重新发明轮子,因为为此我可以实现 OAuth2。我想要更轻的解决方案。 - 为身份验证创建新模块并将其仅包含在需要的资源中是一种很好的做法?
- 你知道关于这个主题的任何好的教程 - 实现 Rails + 设计 + 葡萄?另外,你知道有什么好的吗? 开源的 Rails 项目,就是这样实现的?
- 如何使用更安全的不同方法来实现它?
我为这么长的帖子道歉,但我希望更多的人有同样的问题,它可能会帮助我找到更多关于我的问题的答案。
【问题讨论】:
-
真的,没有人在做同样的事情吗?还是阅读时间太长?天哪……
标签: ruby-on-rails api ruby-on-rails-4 devise grape