Discourse sso 同步登录实现
背景/条件:
- 主站: http://mysite.com (不是真实的)
论坛: http://discourse.mystie.com - 主站服务器技术栈: flask + python3 (不限制,只是用的 工具和方法 会有所不同)
论坛服务器技术栈: rails + ruby (Discource 源码) - 主站已实现: 根据论坛返回的 sso 和 sig, 解密后,配合用户信息加密生成新的相应可用的 sso 和 sig,组成附带登录状态信息的论坛登录 url (sso_login_url)
例如: http://discourse.mysite.com/session/sso_login?sso=xxx&sig=xxx&return_path=/
(即 sso 的基本必须方法) 称为: general_payload_url 方法!!!
参考: Discourse 单点登录功能
实现目标:
sso 登录实现同步登录/注销
- 论坛登录的同时,主站同步登录 (sso 跳转到主站登录页面并返回)
- 论坛注销的同时,主站同步注销 (sso 跳转到主站注销页面并返回)
- 主站登录的同时,论坛自动登录 (不跳转到论坛)
- 主站注销的同时,论坛自动注销 (不跳转到论坛)
关键参数说明:
-
_t
: cookie 中的键值,即用户 token,由 ruby::SecureRandom 生成的一个16位随机数。- 作用: 论坛登录后,会自动生成,存在浏览器 cookie 中,只要不退出登录,可以复制它的值到其他地方(浏览器), 写入到 cookie,就可以登录。
- 如果这个安全问题不影响大碍,那么这就可以利用起来。
前期准备:
- 主站供论坛 sso 登录页面( 主站 login 页面) http://mysite.com/login (也可以修改 html 元素,在论坛创建登录的对话框,仅供参考) 在 /admin 页面设置 sso_url 和 logout_url,开启 sso
- 主站供论坛注销时重定向的注销页面( 主站 logout 页面) http://mysite.com/logout
-
将论坛 cookie 的 _t 字段的 domain (所属域名) 改为 mysite.com 。
- 作用: 那么论坛和主站就可以共享这个 _t 字段了!这时实现同步登录的关键!!涉及 二级域名和一级域名共享 cookie 的问题
- (可以看一下 https://segmentfault.com/a/1190000006932934)
- 修改源码:
cd /var/discourse (discourse 代码存放目录)
./launcher enter app
cd lib/auth/
vim default_current_user_provider.rb
在 162 行后面加上一个逗号: ,
在 162 至 163 行之间新加一行 domain: 'mysite.com’
修改后代码: (158 - 164 行)
hash = {
value: unhashed_auth_token,
httponly: true,
expires: SiteSetting.maximum_session_age.hours.from_now,
secure: SiteSetting.force_https,
domain: "mysite.com"
}
设计思路:
1. 论坛登录
论坛点击登录,跳转到主站 http://mysite.com/login, 附带 sso 和 sig 参数信息(实际跳转到 http://mysite.com/login?sso=xxx&sig=xxx)
操作: 用户输入登录信息( 用户名/密码 )进行登录后,调用 general_payload_url 方法,重新生成新的 sso 和 sig,组成论坛登录 url (sso_login_url),跳转过去。
2. 主站登录
主站点击登录,用户输入登录信息进行登录后,想方法生成并获取 cookie 的 _t 字段 (浏览器不跳转到论坛)
操作:
- 主站服务器后台访问 http://discourse.mystie.com/session/sso,论坛会自动重定向到 login 页面 http://mysite.com/login,附带 sso 和 sig 参数信息
- (http://mysite.com/login?sso=xxx&sig=xxx)
- python 代码:
res = requests.get("http://discourse.mystie.com/session/sso", timeout=10)
sso_url = res.url # sso_url = http://mysite.com/login?sso=xxx&sig=xxx
- 这时调用 general_payload_url 方法,生成新的论坛登录 url (sso_login_url)
- python 代码
sso_login_url = general_payload_url(sso_url)
# sso_login_url = http://discourse.mysite.com/session/sso_login?sso=xxx&sig=xxx&return_path=/
- 使用可以获取 cookie 的方法后台访问 这个论坛登录 url (sso_login_ur),那么就可以从 cookie 中获得 _t 这个字段的值
- python 代码:
import urllib.request
import http.cookiejar
def get_cookie_t(sso_login_url):
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open(sso_login_url, timeout=10)
_t = None
for item in cookie:
if item.name == '_t':
_t = item.value
break
return _t
- 将 _t 写进浏览器 cookie 中,设置 domain 为 mysite.com , 那么论坛就同步登录了。
3. 论坛注销
论坛点击注销,跳转到主站 http://mysite.com/logout
操作: 主站注销用户,并删除浏览器 cookie 中的 _t 字段,返回(论坛)。不删除 _t 会出错!
4. 主站注销
站点击注销 (浏览器不跳转到论坛)
操作: 注销用户后,删除 cookie 中 _t 字段,那么论坛也同步注销了。