Rails:从ActiveResource调用中设计身份验证 [英] Rails: Devise Authentication from an ActiveResource call
问题描述
我的两个轨道应用程序(app1,app2)正在使用活动资源进行通信。
app1调用app2在app2中创建一个用户。 app2将创建该用户,并希望app1然后将用户重定向到app2的验证页面。
从app1到app2将始终要求用户登录。 >
我正在寻找一种方法来避免在app2中的登录步骤,而是使用户在第一次活动资源调用期间登录以创建用户,并以某种方式获取认证令牌。
使用Devise进行身份验证。有没有什么内置的Devise支持这个?
以认证令牌的方式传递?
您正在尝试实施单点登录服务(SSO)的形式(使用app1登录,并通过app2,app3 ...自动验证)。不幸的是不是一件小事。你可能可以使它工作(也许你已经做到了),但是不是试图重塑轮子,而是为了整合现有的解决方案?或者更好的是,标准协议?这实际上比较容易。
CAS服务器
RubyCAS 是实现耶鲁大学CAS(中央认证服务)协议的Ruby服务器。我有很大的成功。
棘手的部分是让它与现有的Devise身份验证数据库一起使用。我们遇到同样的问题,经过一些代码潜水,我想出了以下这些,这对我们来说是一种魅力。这在您的RubyCAS服务器配置中,默认情况下为 /etc/rubycas-server/config.yml
。当然,根据需要进行调整:
认证者:
类:CASServer :: Authenticators :: SQLEncrypted
数据库:
适配器:sqlite3
数据库:/path/to/your/devise/production.sqlite3
user_table:users
username_column:电子邮件
password_column:encrypted_password
encrypt_function:'requirebcrypt; user.encrypted_password == :: BCrypt :: Engine.hash_secret(#{@ password},:: BCrypt :: Password.new(user.encrypted_password).salt)'
在此输入代码
encrypt_function
很难想出来。 ..我不太高兴在这里嵌入一个 require
语句,但嘿,它的工作原理。
CAS客户端
对于客户端(您所在的模块将要集成到app2,app3 ...)中,Rails插件由 RubyCAS客户端 gem。
您将需要一个初始化程序 rubycas_client.rb
,类似于:
require'casclient'
require'casclient / frameworks / rails / filter'
CASClient :: Frameworks :: Rails :: Filter.configure(
:cas_base_url =>https://cas.example.com/
)
最后,您可以重新连接几个Devise电话以使用CAS,以便您当前的代码将按原样工作:
#强制身份验证
def authenticate_user!
CASClient :: Frameworks :: Rails :: Filter.filter(self)
end
#可选身份验证(不在Devise中)
def authenticate_user
CASClient :: Frameworks :: Rails :: GatewayFilter
end
def user_signed_in?
会话[:cas_user] .present?
end
不幸的是没有直接的方法来替换 current_user
,但您可以尝试以下建议:
具有直接数据库访问权限的current_user
如果您的客户端应用程序可以访问后端用户数据库,则可以从中加载用户数据:
def current_user $ b $如果session [:cas_user] .nil?
return User.find_by_email(session [:cas_user])
end
但是对于更可扩展的架构,您可能希望将应用程序与后端分开。为此,您可以尝试以下两种方法。
current_user使用CAS extra_attributes
使用提供的extra_attributes CAS协议:基本上,将所有必需的用户数据作为extra_attributes传递到CAS标记中(在<$ c $中添加一个 extra_attributes
键,列出所需的属性到您的身份验证者c> config.yml ),并在客户端重建虚拟用户。代码看起来像这样:
def current_user
如果session [:cas_user]返回nil。
email = session [:cas_user]
extra_attributes = session [:cas_extra_attributes]
user = VirtualUser.new(:email => email,
:name => extra_attributes [ :name],
:mojo => extra_attributes [:mojo],
)
return user
end
VirtualUser类定义作为一个练习。提示:使用无桌面ActiveRecord(请参阅 Railscast#193 )应该让您编写一个下拉菜单,
current_user在后端使用XML API和ActiveResource
另一种可能性是在用户后端准备XML API,然后使用ActiveResource检索您的用户模型。在这种情况下,假设您的XML API接受一个电子邮件参数过滤用户列表,代码将如下所示:
def current_user
return nil if session [:cas_user] .nil?
email = session [:cas_user]
#这里用户是一个ActiveResource
返回User.all(:params => {:email => email})首先
结束
虽然此方法需要额外的请求,我们发现它是最灵活的。只需确保您的XML API安全,或者您可能会在系统中打开一个缺陷安全漏洞。 SSL,HTTP身份验证,并且由于仅供内部使用,因此可以很好地衡量IP限制。
奖励:其他框架也可以加入乐趣!
由于CAS是一种标准协议,您可以获得允许使用其他技术的应用程序使用单点登录服务的附加好处。有 Java 的官方客户, PHP ,。Net 和 Apache
让我知道这是否有任何帮助,并且不要犹豫,问你是否有任何问题。
My two rails applications(app1, app2) are communicating using active resource.
app1 calls app2 create a user inside app2. app2 would create the user and would like app1 then redirect the user to app2's authenticated pages.
going from app1 to app2 would invariably ask the user to log in.
I was looking for a way to avoid this login step in app2, instead make the user log in during the first active resource call to create user, and somehow get the authentication token written.
Authentication is done using Devise. Is there anything built into Devise that support this?
Is passing around the authentication token the way to go?
You are trying to implement a form of Single Sign-On service (SSO) (sign in with app1, and be automatically authenticated with app2, app3...). It is unfortunately not a trivial task. You can probably make it work (maybe you already did), but instead of trying to reinvent the wheel, why not instead integrate an existing solution? Or even better, a standard protocol? It is actually relatively easy.
CAS server
RubyCAS is a Ruby server that implements Yale University's CAS (Central Authentication Service) protocol. I had great success with it.
The tricky part is getting it to work with your existing Devise authentication database. We faced the same problem, and after some code diving, I came up with the following, which works like a charm for us. This goes in your RubyCAS server config, by default /etc/rubycas-server/config.yml
. Of course, adapt as necessary:
authenticator:
class: CASServer::Authenticators::SQLEncrypted
database:
adapter: sqlite3
database: /path/to/your/devise/production.sqlite3
user_table: users
username_column: email
password_column: encrypted_password
encrypt_function: 'require "bcrypt"; user.encrypted_password == ::BCrypt::Engine.hash_secret("#{@password}", ::BCrypt::Password.new(user.encrypted_password).salt)'
enter code here
That encrypt_function
was pain to figure out... I am not too happy about embedding a require
statement in there, but hey, it works. Any improvement would be welcome though.
CAS client(s)
For the client side (module that you will want to integrate into app2, app3...), a Rails plugin is provided by the RubyCAS-client gem.
You will need an initializer rubycas_client.rb
, something like:
require 'casclient'
require 'casclient/frameworks/rails/filter'
CASClient::Frameworks::Rails::Filter.configure(
:cas_base_url => "https://cas.example.com/"
)
Finally, you can re-wire a few Devise calls to use CAS so your current code will work almost as-is:
# Mandatory authentication
def authenticate_user!
CASClient::Frameworks::Rails::Filter.filter(self)
end
# Optional authentication (not in Devise)
def authenticate_user
CASClient::Frameworks::Rails::GatewayFilter
end
def user_signed_in?
session[:cas_user].present?
end
Unfortunately there is no direct way to replace current_user
, but you can try the suggestions below:
current_user with direct DB access
If your client apps have access to the backend users database, you could load the user data from there:
def current_user
return nil if session[:cas_user].nil?
return User.find_by_email(session[:cas_user])
end
But for a more extensible architecture, you may want to keep the apps separate from the backend. For the, you can try the following two methods.
current_user using CAS extra_attributes
Use the extra_attributes provided by the CAS protocol: basically, pass all the necessary user data as extra_attributes in the CAS token (add an extra_attributes
key, listing the needed attributes, to your authenticator in config.yml
), and rebuild a virtual user on the client side. The code would look something like this:
def current_user
return nil if session[:cas_user].nil?
email = session[:cas_user]
extra_attributes = session[:cas_extra_attributes]
user = VirtualUser.new(:email => email,
:name => extra_attributes[:name],
:mojo => extra_attributes[:mojo],
)
return user
end
The VirtualUser class definition is left as an exercise. Hint: using a tableless ActiveRecord (see Railscast #193) should let you write a drop-in replacement that should just work as-is with your existing code.
current_user using an XML API on the backend and an ActiveResource
Another possibility is to prepare an XML API on the users backend, then use an ActiveResource to retrieve your User model. In that case, assuming your XML API accepts an email parameter to filter the users list, the code would look like:
def current_user
return nil if session[:cas_user].nil?
email = session[:cas_user]
# Here User is an ActiveResource
return User.all(:params => {:email => email}).first
end
While this method requires an extra request, we found it to be the most flexible. Just be sure to secure your XML API or you may be opening a gapping security hole in your system. SSL, HTTP authentication, and since it is for internal use only, throw in IP restrictions for good measure.
Bonus: other frameworks can join the fun too!
Since CAS is a standard protocol, you get the added benefit of allowing apps using other technologies to use your Single Sign-On service. There are official clients for Java, PHP, .Net and Apache.
Let me know if this was of any help, and don't hesitate to ask if you have any question.
这篇关于Rails:从ActiveResource调用中设计身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!