我在确定如何为Rails API创建基于令牌的身份验证系统方面遇到了一些麻烦。
我计划构建一个Ember应用,用户应该可以通过提供密码和用户名登录。
在导轨方面,我设置了一个AuthenticationToken
模型,用于检查授权令牌和非持久密钥。我根据secret和AuthenticationToken id(BSON :: ObjectId)存储加密的秘密。
基本上我的想法是,只要客户端挂起秘密,客户端就应该能够进行身份验证。然后它需要请求一个新的访问令牌。我希望通过避免会话来保持无国籍。
我很困惑路由和控制器在用户交换用户/传递访问令牌的位置应该是什么样的。正如我发现的众多博客文章/教程中遗漏了那一部分。我只是创建一个简单的POST路由,客户端发送凭据并将令牌作为JSON返回?或者我应该使用HTTP BASIC AUTH?
class User
include Mongoid::Document
include ActiveModel::SecurePassword
embeds_many :tokens, class_name: 'Users::AuthenticationToken'
field :email, type: String
field :username, type: String
field :password_digest, type: String
validates :email, presence: true, uniqueness: true
has_secure_password
end
require "securerandom"
require "bcrypt"
class Users::AuthenticationToken
include Mongoid::Document
embedded_in :user
attr_accessor :secret
field :hashed_secret
validates :hashed_secret, uniqueness: true, presence: true
before_validation :generate_secret
def self.find_by_id(id)
begin
id = BSON::ObjectId.from_string(id) unless id.is_a?(BSON::ObjectId)
tokens = User.find_by('tokens._id' => id).try(:tokens)
tokens ? tokens.find_by(id: id) : nil
rescue Mongoid::Errors::DocumentNotFound, BSON::ObjectId::Invalid
nil
end
end
# @return [Boolean]
def has_secret? secret
BCrypt::Password.new(hashed_secret) == secret
end
def authenticate!(secret)
user if has_secret? secret
end
private
def generate_secret
self.secret = SecureRandom.urlsafe_base64(32)
self.hashed_secret = BCrypt::Password.create secret, cost: cost
end
def cost
Rails.env.test? ? 1 : 10
end
end
我还有一个看起来像这样的身份验证方法:
def authenticate!
token = Users::AuthenticationToken.find_by_id(token_id)
user = token.try(:authenticate!, secret)
user.nil? ? fail!("Could not log in") : success!(user)
end