我正在为LDAP身份验证开发自定义策略,并且我已根据http://kyan.com/blog/2013/10/11/devise-authentication-strategies和此设计wiki实施了valid?
和authenticate!
方法。我的自定义策略独立运作。
我也想使用Lockable模块(和其他Devise模块),但它们似乎只在Database_Authenticatable也存在的情况下才有用。我一直在阅读Devise源代码,但我不明白Lockable如何能够找到用户(资源)以增加:failed_attempts计数。如何使用自定义策略将用户记录或资源传递给Lockable?
我的自定义策略如下。我正在使用Devise 3.5和Rails 4.2
require 'net/ldap'
require 'devise/strategies/authenticatable'
module Devise
module Strategies
class LdapAuthenticatable < Authenticatable
def valid?
params[:user] && login.present? && password.present?
end
def authenticate!
resource = mapping.to.find_for_authentication(email: params[:user][:email])
if params[:user]
begin
ldap = Net::LDAP.new
ldap.auth(my_ldap_service, my_ldap_service_password)
ldap.encryption(:simple_tls)
ldap.base = "base_here"
ldap.host = my_ldap_host
ldap.port = "636"
result_attrs = ["employeenumber", "givenname", "sn", "mail", "department"]
result = ldap.bind_as(base: "base_here", filter: "(mail=#{login})", attributes: result_attrs, password: password, time: 3)
if result
#valid ldap credentials
user = get_user_from_database(result)
if user
success!(user)
else
fail!(:invalid_login)
end
else
fail!(:invalid_login)
end
rescue => e
Rails.logger.error e.message
fail!(:invalid_login)
end
end
end
def login
params[:user][:email]
end
def password
params[:user][:password]
end
end
end
end
答案 0 :(得分:0)
Active Directory应该处理可锁定的功能。通过在自定义策略中使用LDAP,可以将功能的责任从应用程序转移到LDAP / Active Directory。可锁定应防止恶棍强行访问您的应用程序。如果Active Directory对此没有限制,他可以直接针对它进行攻击,如果成功,则使用应用程序中的凭据。
但是,您应该启用应用程序以显示远程服务器返回的错误消息,例如“重试次数过多,请在5分钟后重试”。
答案 1 :(得分:0)
我最终结合使用devise_custom_authenticatable和原始问题上发布的资源。为使锁定功能正常工作,必须存在database_authenticable。如果您拥有对LDAP的控制权,那么Toni的answer应该可以使用,但是当我提出问题时却没有。
答案 2 :(得分:0)
您缺少调用 validate
方法,该方法是您继承的 Authenticatable 类的一部分。 validate
方法从可锁定模块调用 valid_for_authentication?
方法,然后该方法负责增加尝试次数并锁定帐户。
在 database_authenticable 中,这通过使用返回布尔值的 valid_password?
解决了 here。在您的情况下,您需要在 validate(resource) { false }
不返回结果的情况下添加 bind_as
。
固定的完整 authenticate!
方法如下所示
def authenticate!
resource = mapping.to.find_for_authentication(email: params[:user][:email])
if params[:user]
begin
ldap = Net::LDAP.new
ldap.auth(my_ldap_service, my_ldap_service_password)
ldap.encryption(:simple_tls)
ldap.base = "base_here"
ldap.host = my_ldap_host
ldap.port = "636"
result_attrs = ["employeenumber", "givenname", "sn", "mail", "department"]
result = ldap.bind_as(base: "base_here", filter: "(mail=#{login})", attributes: result_attrs, password: password, time: 3)
if result
#valid ldap credentials
user = get_user_from_database(result)
if user
success!(user)
else
fail!(:invalid_login)
end
else
# no result -> wrong password
validate(resource) { false }
fail!(:invalid_login)
end
rescue => e
Rails.logger.error e.message
fail!(:invalid_login)
end
end
end