用户认证与葡萄和设计 [英] User Authentication with Grape and Devise
问题描述
我有困难的理解,并正确实施 用户验证的中的API。换句话说,我有严重的问题,了解葡萄API与前端框架,如Backbone.js的,AngularJS或Ember.js的整合。
I have difficulties to understand and also properly implement User Authentication in APIs. In other words, I have serious problem to understand the integration of Grape API with front-end frameworks such as Backbone.js, AngularJS or Ember.js.
我试图转动所有不同的方法和读了很多关于这一点,但谷歌返回我真正坏的资源,在我看来,像有这个主题真的没有好文章 - Rails和用户认证与设计和前端框架的
I'm trying to pivot all different approaches and read a lot about that, but Google returns me truly bad resources and it seems to me, like there is no really good article on this topic - Rails and User authentication with Devise and front-end frameworks.
我将描述我目前的支点,我希望你能提供我一些反馈我的实现,也许我指向正确的方向。
I will describe my current pivot and I hope you can provide me some feedback on my implementation and maybe point me to the right direction.
当前实施
我有后台的 Rails的REST API 与下面的的的Gemfile 的(我会故意缩短所有文件code)
I have backend Rails REST API with following Gemfile(I will purposely shorten all file code)
gem 'rails', '4.1.6'
gem 'mongoid', '~> 4.0.0'
gem 'devise'
gem 'grape'
gem 'rack-cors', :require => 'rack/cors'
我的当前实现仅具有以下路线的API( 的routes.rb 的)
My current implementation has only APIs with following Routes(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 的
I created have following model 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 的)
In my controllers I created following folder structure: controllers->api->v1 and I have created following shared module Authentication (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
所以每次当我想确保,我的资源将身份验证令牌被调用,我可以简单地调用补充一点:包括API :: V1 ::验证
的葡萄资源:
module API
module V1
class Posts < Grape::API
include API::V1::Defaults
include API::V1::Authentication
现在我有另一个葡萄资源被称为用户(users.rb的)这里我实现了认证,注册和注销的方法。(我想我在这里混苹果梨,我就提取登录/注销过程到另一个葡萄资源 - 会话)
Now I have another Grape resource called Users(users.rb) and here I implement methods for authentication, registration and logout.(I think that I mix here apples with pears, and I should extract the login/logout process to another Grape resource - Session).
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
我意识到我present这里每吨code,并且它可能没有什么意义,但是这是我目前有,我可以使用 authentication_token
反对我的API调用这是由模块保护验证
。
I realize that I present here a ton of code and it might not make sense, but this is what I currently have and I'm able to use the authentication_token
for calls against my API which are protected by module Authentication
.
我觉得这个解决方案并不好,但我真的在寻找更简单的方式如何实现通过API的用户认证。我有我下面列出几个问题。
I feel like this solution is not good, but I really looking for easier way how to achieve user authentication through APIs. I have several questions which I listed below.
问题
- 请您觉得这种实现是很危险的,如果是这样,为什么? - 我认为,这是因为一个令牌的使用情况。有没有一种方法如何改善这种情况?我也看到有单独的模型
令牌
有到期时间等实现,但我认为这几乎是像重新发明轮子,因为这个目的,我可以实现的OAuth2。我想有更轻的解决方案。 - 这是很好的做法,为验证创建新的模块,并将其包含到仅在需要的地方的资源?
- 请您了解关于这个主题的很好的教程 - 实施
导轨+设计+葡萄?此外,你知道任何好的
开源Rails项目,这是这种方式实现? - 如何用不同的方法实现它哪个更安全?
- Do you think this kind of implementation is dangerous, if so, why? - I think that it is, because of the usage of one token. Is there a way how to improve this pattern? I've also seen implementation with separate model
Token
which has expiration time, etc. But I think this is almost like reinventing wheel, because for this purpose I can implement OAuth2. I would like to have lighter solution. - It is good practice to create new module for Authentication and include it only into resources where it is needed?
- Do you know about any good tutorial on this topic - implementing Rails + Devise + Grape? Additionally, do you know about any good open-source Rails project, which is implemented this way?
- How can I implement it with different approach which is more safer?
我这么长的帖子道歉,但我希望更多的人有同样的问题,这可能会帮助我找到我的问题更多的答案。
I apologize for such a long post, but I hope that more people has the same problem and it might help me to find more answers on my questions.
推荐答案
添加token_authenticable制定模块(适用于色器件版本&LT; = 3.2)
Add token_authenticable to devise modules (works with devise versions <=3.2)
在user.rb加:token_authenticatable制定的模块列表,它应该是这个样子如下:
In user.rb add :token_authenticatable to the list of devise modules, it should look something like below:
class User < ActiveRecord::Base
# ..code..
devise :database_authenticatable,
:token_authenticatable,
:invitable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable
attr_accessible :name, :email, :authentication_token
before_save :ensure_authentication_token
# ..code..
end
在生成令牌验证自己(如果色器件版本> 3.2)
Generate Authentication token on your own (If devise version > 3.2)
class User < ActiveRecord::Base
# ..code..
devise :database_authenticatable,
:invitable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable
attr_accessible :name, :email, :authentication_token
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
添加迁移authentiction令牌
Add migration for authentiction token
rails g migration add_auth_token_to_users
invoke active_record
create db/migrate/20141101204628_add_auth_token_to_users.rb
编辑迁移文件中加入:authentication_token列给用户
Edit migration file to add :authentication_token column to users
class AddAuthTokenToUsers < ActiveRecord::Migration
def self.up
change_table :users do |t|
t.string :authentication_token
end
add_index :users, :authentication_token, :unique => true
end
def self.down
remove_column :users, :authentication_token
end
end
运行迁移
耙分贝:迁移
生成现有用户令牌
我们需要调用保存的用户,以确保认证令牌每个实例是为每个用户present。
We need to call save on every instance of user that will ensure authentication token is present for each user.
User.all.each(安培;:保存)
使用安全葡萄API身份验证令牌
Secure Grape API using auth token
您需要低于code加入到API ::根在为了增加基于令牌的认证。如果你还没有意识API ::根的话,请用葡萄阅读构建RESTful API
You need to add below code to the API::Root in-order to add token based authentication. If you are unware of API::Root then please read Building RESTful API using Grape
在下面的例子中,我们根据两个方案认证用户 - 如果用户登录到Web应用程序,然后使用相同的会话 - 如果会议不提供身份验证令牌传递然后基于令牌查找用户p>
In below example, We are authenticating user based on two scenarios – If user is logged on to the web app then use the same session – If session is not available and auth token is passed then find user based on the token
# lib/api/root.rb
module API
class Root < Grape::API
prefix 'api'
format :json
rescue_from :all, :backtrace => true
error_formatter :json, API::ErrorFormatter
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
mount API::V1::Root
mount API::V2::Root
end
end
这篇关于用户认证与葡萄和设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!