我目前在我的应用程序控制器中有一个用于凭据验证的before_filter。我还希望在直接访问 url 时能够验证用户凭据(即,从脚本而不是从浏览器登录表单/会话)。通过 GET 请求接受用户凭据是否存在问题?
application_controller.rb
before_filter :login_required
def login_required
reset_session if session[:last_seen] < Rails.configuration.admin_timeout.minutes.ago rescue nil
return true if session[:user]
if (!params[:login].nil? && !params[:password].nil?) && session[:user] = User.authenticate(params[:login], params[:password])
return true
else
render :nothing => true, :status => 401
return false
end
flash[:alert] = 'Please login to continue'
redirect_to login_url and return false
end
如果这不是可接受的做法,我希望只能在某些"安全"控制器操作上执行此操作。所以例如:
product_controller.rb
before_filter :do_something
permit_login_via_get :only => [:index]
def index
# Do some stuff in here. This should be accessible via http://mydomain.com/products?login=admin&password=yeahlikeidtellyouthat
end
所以现在我的login_requred函数需要修改,以便"permit_login_via_get"方法使用它。
问题是凭据将在 URL 中传递,因此非常明显,并且可能也缓存在用户的浏览器历史记录中。
所以我考虑了一下,决定最好为我的每个应用程序创建API密钥,并使用Net::HTTP请求设置一个包含API密钥的自定义标头。这样,就没有通过 URL 发送的用户凭据。现在,关于这种方法的"安全"与否,或者我需要做些什么来使其安全是另一回事。但这是我到目前为止的做法,它似乎正在工作:
application.rb (Manager application)
# API Keys
config.api_key = "somelongstringofcharacters"
config.pos_api_key = "anotherlongstringofcharacters"
config.website_api_key = "yetANOTHERlongstringofcharacters"
POS和网站应用程序中的应用程序.rb与上述示例相同。
application_controller.rb(所有应用程序)
before_filter :login_required
def login_required
reset_session if session[:last_seen] < Rails.configuration.admin_timeout.minutes.ago rescue nil
return true if session[:user]
unless request.headers["X-API-Key"].nil?
if request.headers["X-API-Key"] == Rails.configuration.api_key
return true
else
render :nothing => true, :status => 401
return false
end
end
flash[:alert] = 'Please login to continue'
redirect_to :controller => :users, :action => :login
return false
end
这样,所有方法都是私有的(除了那些具有 skip_before_filter :login_required 的方法),如果您通过使用表单使用登录名/密码组合通过会话登录,或者如果您发出的请求具有具有正确应用程序密钥的"X-API-Key"标头,则可以访问。
下面是通过管理器应用程序运行的示例 Rake 任务;它从受 login_required 方法保护的 POS 服务器请求 JSON 文件:
desc "Test Task so that larger projects don't need to be run through"
task :testing => :environment do
require "net/http"
require "uri"
url = URI.parse("http://myposdomain/items.json?all=true&category=Wines")
req = Net::HTTP::Get.new(url.path)
req.add_field("X-API-Key", Rails.configuration.pos_api_key)
res = Net::HTTP.new(url.host, url.port).start do |http|
http.request(req)
end
myposdomain_wines = res.body
end