折衷的方式实现php与ruby共享session实现单点登录

2016-12-16 10:10:25来源:oschina作者:游鱼彩虹人点击

第七城市

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


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对象与序列化之前不一样。


undefined method `sweep' for {"notice"=>"Logged in successfully."}:Hash

在网上搜索很久,一个日文的blog提出了解决方案:Rails sessionのシリアライズにJSONが使われない理由: なぜMarshal? JSON/YAMLの罠


主要是在反序列化的时候加了这么一句:


if original.has_key?('flash')
original['flash'] = ActionDispatch::Flash::FlashHash.new.update(original['flash'])
end

3.3.折衷的方式


这么深度的修改对于这个需求似乎太复杂了,最后还是决定用简单些的方式,利用ruby开放一个session的json接口,php通过调用接口获得用户信息,修改的地方很少:


ruby


class ActiveController < ApplicationController
def show
render :json => current_user
end
end

php


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
//...
第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台