您可以考虑使用doorkeeper gem来进行API授权。我曾考虑过它,但由于复杂性和缺乏文档而放弃了,因为在我的用例中无法使其正常工作。简而言之,我无法让其正常工作。
有一篇很好的
文章介绍了如何使用Warden进行身份验证,而不需要Devise,这应该能让您了解身份验证系统的关键部分。Devise不适用于API身份验证,事实上,Devise最近删除了可能对API有用的唯一一个东西,即基于令牌的身份验证,显然API不是他们路线图的一部分!
我使用上述引用文章中的指南创建了自己的JSON-only Warden策略,使用OAUTH 2 Owner Password Credentials授权类型(请参见
RFC 6749)生成并返回用于以后API请求的Bearertoken。 API客户端可以轻松创建JSON以执行此类身份验证以获取授权访问令牌。
我将提供一些Rails代码供您入门,但您必须将其集成到特定环境中。不提供保修 :)
Warden初始化程序:
Dir["./app/strategies/warden/*.rb"].each { |file| require file }
Rails.application.config.middleware.insert_after ActionDispatch::ParamsParser, Warden::Manager do |manager|
manager.default_strategies :null_auth, :oauth_access_token, :oauth_owner_password
manager.failure_app = UnauthorizedController
end
OAUTH 2密码认证的狱卒策略:
module Warden
class OauthOwnerPasswordStrategy < Strategies::Base
def valid?
return false if request.get?
params['grant_type'] == 'password' && params['client_id'] == 'web' && ! params['username'].blank?
end
def authenticate!
user = User.with_login(params['username']).first
if user.nil? || user.confirmed_at.nil? || ! user.authenticate!(params['password'])
sleep(SecureRandom.random_number(20) / 1000.0)
fail! :message => 'strategies.password.failed'
else
success! user, store: false
end
end
end
Strategies.add(:oauth_owner_password, OauthOwnerPasswordStrategy)
end
OAUTH 2访问令牌认证的Warden策略:
module Warden
class OauthAccessTokenStrategy < Strategies::Base
def valid?
return false unless auth_header = request.headers['authorization']
auth_header.split(' ')[0] == 'Bearer'
end
def authenticate!
Token.expired.delete
token = Token.active.first(purpose: 'access', token: request.headers['authorization'].split(' ')[1])
if token && (user = token.user) && user.confirmed_at
success! user, store: false
else
sleep(SecureRandom.random_number(20) / 1000.0)
fail! message: 'strategies.oauth_access_token.failed'
end
end
end
Strategies.add(:oauth_access_token, OauthAccessTokenStrategy)
end
空认证策略(在开发中可能会有用,只需在config/environments/development.rb中设置config.null_auth_user
):
module Warden
class NullAuthStrategy < Strategies::Base
def valid?
! Rails.configuration.null_auth_user.blank?
end
def authenticate!
user = User.with_login(params["username"]||Rails.configuration.null_auth_user).first
if user.nil?
fail! :message => "strategies.password.failed"
else
success! user, store: false
end
end
end
Strategies.add(:null_auth, NullAuthStrategy)
end
JSON客户端的狱卒失败应用(使用裸金属Rails控制器):
class UnauthorizedController < ActionController::Metal
def self.call(env)
@respond ||= action(:respond)
@respond.call(env)
end
def respond(env)
self.status = 401
self.content_type = 'json'
self.response_body = { 'errors' => ['Authentication failure']}.to_json
end
end
在您的基本API控制器中添加以下内容:
before_filter :authenticate!
protected
helper_method :warden, :signed_in?, :current_user
def warden
request.env['warden']
end
def signed_in?
!current_user.nil?
end
def current_user
@current_user ||= warden.user
end
def authenticate!(*args)
warden.authenticate!(*args)
end
一个会话控制器:
class SessionsController < ApiController
skip_before_filter :authenticate!
def create
authenticate!(scope: :oauth_owner_password, store: false)
if signed_in?
token = Token.create! purpose: 'access',
user: current_user,
expires_in: Rails.configuration.session_lifetime
response.headers["Cache-Control"] = "no-store"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
render json: {
access_token: token.token,
token_type: 'Bearer',
expires_in: token.expires_in,
scope: 'user'
}
end
end
def destroy
Token.current.delete
warden.logout
head :no_content
end
end
你需要定义自己的用户模型和令牌模型来跟踪用户和承载令牌,令牌模型需要有一个名为
active
的范围来将结果集限制为未过期的令牌。令牌生成应使用
SecureRandom.urlsafe_base64
。