php与ruby共享session实现单点登录
1.背景
前一阵部门要做一个内部讨论区,希望能和原有的gitlab集成在一起。
discuz虽然成熟但是感觉不够高大上,找了几个ruby的论坛discourse,rabel虽然时髦值够了但是成熟度又缺了点,最后选了php的question2answer作为论坛程序,采用iframe的方式嵌入原来的gitlab程序。
2.单点登录方案
2.1.共享cookie
- 在cookie里保存user的信息,php读cookie并取得用户信息。
- ruby和php程序要在同一个域名下
- cookie在客户端是可见的,一般要对cookie进行加密。
- 数据不安全
2.2.共享session
- 在cookie里保存ruby session信息,php获得session信息后读session数据。
- ruby和php程序要在同一个域名下
- session内容客户端不可见。
2.3.cas
- 通过统一认证服务登录
- ruby和php登录时重定向至认证服务器,通过验证后回调相应服务保存token,之后只要具体服务向cas请求token是否过期就可以判断是否登录。
由于两个业务在同一个域名下,这里就放弃了复杂的cas方案;考虑源代码安全,没有用cookie存储用户信息,最后决定使用共享session的方式实现单点登录
3.通过session单点登录
3.1.rails与session
gitlab使用devise作为登录框架,关于session的配置在config/initilizers/sessions.rb下,默认使用redis方式保存session
1 2 3 4 5 6 7 8 |
Gitlab::Application.config.session_store( :redis_store, # Using the cookie_store would enable session replay attacks. servers: Gitlab::Application.config.cache_store.last, # re-use the Redis config from the Rails cache store key: '_gitlab_session', secure: Gitlab.config.gitlab.https, httponly: true, path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root ) |
这里也可以改成在数据库或者memcached里存储,存储格式与redis类似,不多讲了。
redis里key为session id,value为序列化后的数据,默认使用的序列化算法为marshal,理论上只要php读出内容来就可以取得session数据了。
3.2.ruby与序列化
不幸的是,php里没有能够直接反序列化marshal对象的的方法。
最初考虑把marshal改为json方式存储,需要修改redis-store的一些代码,主要是覆盖源代码中的marshal和unmarshal函数,替换为json实现。具体可以参考:Sharing Rails sessions with PHP, ColdFusion, and more!
不过用这个方法出现了一些问题:marshal序列话会保存对象的一些meta信息,json是没有这些信息的,导致反序列化之后的ruby对象与序列化之前不一样。
1 2 |
undefined method `sweep' for {"notice"=>"Logged in successfully."}:Hash |
在网上搜索很久,一个日文的blog提出了解决方案:Rails sessionのシリアライズにJSONが使われない理由: なぜMarshal? JSON/YAMLの罠
主要是在反序列化的时候加了这么一句:
1 2 3 |
if original.has_key?('flash') original['flash'] = ActionDispatch::Flash::FlashHash.new.update(original['flash']) end |
3.3.折衷的方式
这么深度的修改对于这个需求似乎太复杂了,最后还是决定用简单些的方式,利用ruby开放一个session的json接口,php通过调用接口获得用户信息,修改的地方很少:
ruby
1 2 3 4 5 |
class ActiveController < ApplicationController def show render :json => current_user end end |
php
1 2 3 4 5 6 7 8 9 10 11 12 |
if ($_COOKIE['_gitlab_session']) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://localhost:8080/active"); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_COOKIE, '_gitlab_session='.$_COOKIE['_gitlab_session']); $filecontent=curl_exec($ch); curl_close($ch); $obj=json_decode($filecontent,true); // handle $obj //... } |
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可,转载请注明作者及原网址。
抱歉,暂停评论。