折衷的方式實現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

   //...
相關文章
相關標籤/搜索