在Authlogic中,有没有办法可以为身份验证方法添加条件?我知道使用 find_by_login_method 我可以指定另一种方法,但是当我使用它时,我需要传递另一个参数,因为 find_by_login_method 方法只传递被认为的参数'login_field'。
我需要做的是检查一下真实模型的关联。这是我想要使用的方法
# make sure that the user has access to the subdomain that they are
# attempting to login to, subdomains are company names
def self.find_by_email_and_company(email, company)
user = User.find_by_email(email)
companies = []
user.brands.each do |b|
companies << b.company.id
end
user && companies.include?(company)
end
但由于只有一个参数发送到find_by_email_and_company方法,因此失败。
该公司实际上是子域名,所以为了得到它,我只是将它放在表格中的隐藏字段中(只有这样我才能想到它才能进入模型)
有没有一种方法我可以以某种方式覆盖..?
使用下面的答案,我提出了以下有效的方法:
用户模型(User.rb)
def self.find_by_email_within_company(email)
# find the user
user = self.find_by_email(email)
# no need to continue if the email address is invalid
return false if user.nil?
# collect the subdomains the provided user has access to
company_subdomains = user.brands.map(&:company).map(&:subdomain)
# verify that the user has access to the current subdomain
company_subdomains.include?(Thread.current[:current_subdomain]) && user
end
应用程序控制器
before_filter :set_subdomain
private
def set_subdomain
# helper that retreives the current subdomain
get_company
Thread.current[:current_subdomain] = @company.subdomain
end
用户会话模型(UserSession.rb)
find_by_login_method :find_by_email_within_company
我已经阅读了一些关于使用Thread.current和冲突的命名空间的事情。这是一个很好的解决方案,对我有用但很想在赏金到期之前听到任何其他建议,否则,+ 100到Jens Fahnenbruck: )
答案 0 :(得分:2)
Authlogic提供API来处理基于子域的身份验证。
class User < ActiveRecord::Base
has_many :brands
has_many :companies, :through => :brands
acts_as_authentic
end
class Brand < ActiveRecord::Base
belongs_to :user
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :brands
has_many :users, :through => :brands
authenticates_many :user_sessions, :scope_cookies => true
end
会话控制器:
class UserSessionsController < ApplicationController
def create
@company = Company.find(params[:user_session][:company])
@user_session = @company.user_sessions.new(params[:user_session])
if @user_session.save
else
end
end
end
另一方面
以下是使用当前方法解决问题的方法(我会使用第一种方法):
将自定义数据设置为用于创建email
对象的哈希的键UserSession
。
AuthLogic会将此值传递给find_by_login
方法。在find_by_login方法中,访问所需的值。
<强>假设:强>
子域ID在表单中名为company
的字段中设置。
class UserSessionsController < ApplicationController
def create
attrs = params[:user_session].dup #make a copy
attrs[:email] = params[:user_session] # set custom data to :email key
@user_session = UserSession.new(attrs)
if @user_session.save
else
end
end
end
型号代码
您可以简化和优化用于查找具有给定电子邮件和子域的用户的代码,如下所示:
class User < ActiveRecord::Base
def find_by_email params={}
# If invoked in the normal fashion then ..
return User.first(:conditions => {:email => params}) unless params.is_a?(Hash)
User.first(:joins => [:brands => :company}],
:conditions => ["users.email = ? AND companies.id = ?",
params[:email], params[:company]])
end
end
修改1
用户通过身份验证后,系统应提供对授权数据的访问权限。
如果您维护同一个表中所有域的数据,则必须按子域和经过身份验证的用户对数据进行范围调整。
假设您的Post
模型包含company_id
和user_id
列。当用户登录时,您希望显示子域的用户帖子。这是为子域定义用户数据的一种方法:
Posts.find_by_company_id_and_user_id(current_company, current_user)
Posts.for_company_and_user(current_company, current_user) # named scope
如果不对数据进行调整,则系统中可能存在安全漏洞。
答案 1 :(得分:1)
在您的lib文件夹中添加一个包含以下内容的文件:
class Class
def thread_local_accessor name, options = {}
m = Module.new
m.module_eval do
class_variable_set :"@@#{name}", Hash.new {|h,k| h[k] = options[:default] }
end
m.module_eval %{
FINALIZER = lambda {|id| @@#{name}.delete id }
def #{name}
@@#{name}[Thread.current.object_id]
end
def #{name}=(val)
ObjectSpace.define_finalizer Thread.current, FINALIZER unless @@#{name}.has_key? Thread.current.object_id
@@#{name}[Thread.current.object_id] = val
end
}
class_eval do
include m
extend m
end
end
end
我发现了here
然后在控制器中添加代码,如下所示:
class ApplicationController < ActionController
before_filter :set_subdomain
private
def set_subdomain
User.subdomain = request.subdomains[0]
end
end
现在您可以在您的用户模型中执行以下操作(假设您的公司模型有一个名为子域的方法:
class User < ActiveRecord::Base
thread_local_accessor :subdomain, :default => nil
def self.find_by_email_within_company(email)
self.find_by_email(email)
company_subdomains = user.brands.map(&:company).map(&:subdomain)
company_subdomains.include?(self.subdomain) && user
end
end
和FYI:
companies = user.brands.map(&:company).map(&:subdomain)
与
相同companies = []
user.brands.each do |b|
companies << b.company.subdomain
end
答案 2 :(得分:0)
使用rails 3,您可以使用此解决方法:
class UserSessionsController < ApplicationController
...
def create
@company = <# YourMethodToGetIt #>
session_hash = params[:user_session].dup
session_hash[:username] = { :login => params[:user_session][:username], :company => @company }
@user_session = UserSession.new(session_hash)
if @user_session.save
flash[:notice] = "Login successful!"
redirect_back_or_default dashboard_url
else
@user_session.username = params[:user_session][:username]
render :action => :new
end
...
end
然后
class UserSession < Authlogic::Session::Base
find_by_login_method :find_by_custom_login
end
和
class User < ActiveRecord::Base
...
def self.find_by_custom_login(hash)
if hash.is_a? Hash
return find_by_username_and_company_id(hash[:login], hash[:company].id) ||
find_by_email_and_company_id(hash[:login], hash[:company].id)
else
raise Exception.new "Error. find_by_custom_login MUST be called with {:login => 'username', :company => <Company.object>}"
end
end
...
end
这很简单,“正确”。我花了很多时间找出来,但它运作正常!