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

1.背景

前一阵部门要作一个内部讨论区,但愿能和原有的gitlab集成在一块儿。php

discuz虽然成熟可是感受不够高大上,找了几个ruby的论坛discourse,rabel虽然时髦值够了可是成熟度又缺了点,最后选了php的question2answer做为论坛程序,采用iframe的方式嵌入原来的gitlab程序。html

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的方式实现单点登陆git

3.经过session单点登陆

3.1.rails与session

gitlab使用devise做为登陆框架,关于session的配置在config/initilizers/sessions.rb下,默认使用redis方式保存sessionredis

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对象的的方法。json

最初考虑把marshal改成json方式存储,须要修改redis-store的一些代码,主要是覆盖源代码中的marshal和unmarshal函数,替换为json实现。具体能够参考:Sharing Rails sessions with PHP, ColdFusion, and more!安全

不过用这个方法出现了一些问题:marshal序列话会保存对象的一些meta信息,json是没有这些信息的,致使反序列化以后的ruby对象与序列化以前不同。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

   //...
相关文章
相关标签/搜索