Discourse 单点登录功能

需更新

(Erick Guan) #1

本文翻译于官方论坛,于2014-10-29。

Discourse 现在能够在站外验证用户了。

问题

许多站点想要集成 Discourse 论坛,然而同时想要保持用户在另一独立站点注册。在这样的情况下所有登录操作应该被外包至另一个站点。

如果我想共同使用单点登录(SSO)和现有登录方式呢?

SSO 的目的是取代 Discourse 验证,但是如果您想添加一个登录方法插件,请参考: 微博登录插件

启用单点登录(SSO)

为了启用单点登录您需要填写 3 个设置:

enable_sso:必须启用,全局切换开关
sso_url:当用户尝试登录时用户将被引导至的 离站 URL
sso_secret:秘密字符串用于计算 SSO 负荷哈希。以确保负荷是被加密的。

enable_sso 被设置为启动后:

  • 点击登录或者头像将,重定向您至 /session/sso,并最终重定向用户到 sso_url,同时附带一个已签名的负荷。
  • 用户将不被允许“修改密码”。该栏已从用户资料页面删除。
  • 用户将不能够使用 Discourse 验证身份(用户名/密码、Google 等等)

如何我误启动了呢?

如果您不小心选择了 enable_sso 并需要回退至原始状态,但无法访问管理面板时,
请运行:

./launcher enter app
rails c
irb > SiteSetting.enable_sso = false
irb > exit
exit

在您的站点上实现 SSO

Discourse 将重定向客户端至 sso_url,并带一个已签名的负荷:(假设 sso_urlhttps://somesite.com/sso

您将得到以下格式的流量

https://somesite.com/sso?sso=PAYLOAD&sig=SIG

负荷是以 Base64 编码的 nonce。负荷总是一个有效的查询字符串。

例如,nonce 是 ABCD。raw_payload 将是:

nonce=ABCD,这个原始负荷被base 64编码。

被调用的 endpoint 必须

  1. 验证签名,确保 HMAC-SHA256 的 sso_secret 和 PAYLOAD 与 sig 相等
  2. 验证需要的验证操作
  3. 创建一个新的负荷,其中附带 nonceemailexternal_id 和可选的(用户名和名字)
  4. Base64 编码负荷
  5. 计算 HMAC-SHA256 哈希,使用相应的 sso_secret 作为 key,Base 64 编码的负荷作为文本
  6. 重定向回 http://discourse_site/session/sso_login?sso=payload&sig=sig

Discourse 将验证 nonce 是否有效(如果有效它将立即过期,没有人能再使用),它将尝试:

  1. 通过在 SingleSignOnRecord 模型中查找已经存在关联的 external_id
  2. 通过提供的 email 登录用户(并更新 external_id)
  3. 根据提供的用户信息(邮箱,用户名、名字)创建新账户,并更新 external_id

安全考虑

nonce(一次性 token)将自动在 10 分钟后过期。这意味着只要用户被重定向到您的站点,他们有 10 分钟时间登录/创建一个新账户。

这个协议对回放攻击是安全的,因为 nonce 只可以被使用一次。

参考实现

Discourse 包含了一个 SSO 参考实现类:

一个简单的实现可以是:

class DiscourseSsoController < ApplicationController
  def sso
    secret = "MY_SECRET_STRING"
    sso = SingleSignOn.parse(request.query_string, secret)
    sso.email = "user@email.com"
    sso.name = "Bill Hicks"
    sso.username = "bill@hicks.com"
    sso.external_id = "123" # unique to your application
    sso.sso_secret = secret

    redirect_to sso.to_url("http://l.discourse/session/sso_login")
  end
end

迁移至或迁出单点登录

系统总是信任单点登录 endpoint 返回的邮件地址。这意味着如果您在 Discourse 中禁用 SSO 时已经存在的账户可以直接被重新使用,而不需要再创建一个新账户。

如果您关闭了 SSO,用户可以通过重置密码重新获得他们账户的控制权。

现实世界例子:

使用以下设定:

Discourse 域名:http://discuss.example.com
SSO url:http://www.example.com/discourse/sso
SSO secret:d836444a9e4084d5b224a60c208dce14

用户尝试登录

  • Nonce 生成:cb68251eefb5211e58c00ff1395f0c0b

  • 原始负荷生成:nonce=cb68251eefb5211e58c00ff1395f0c0b

  • 负荷被 Base64 编码:bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\n

  • 符合按 URL 格式编码:bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A

  • HMAC-SHA256 根据已经编码的负荷生成:2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56`

最终浏览器被重定向至:

http://www.example.com/discourse/sso?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A&sig=2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56

另一方面

  1. 负荷通过 HMAC-SHA256 被验证,如果 sig 不匹配,流程终止。
  2. 反向执行以上步骤可以获得 nonce。

用户登录信息:

name: sam
external_id: hello123
email: test@test.com
username: samsam
  • 未签名的负荷生成:

nonce=cb68251eefb5211e58c00ff1395f0c0b&name=sam&username=samsam&email=test%40test.com&external_id=hello123

顺序不重要,值要被 URL 编码

  • 负荷被 Base64 编码

"bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z\nYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl\ncm5hbF9pZD1oZWxsbzEyMw==\n

  • 负荷按 URL 编码

bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z%0AYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl%0Acm5hbF9pZD1oZWxsbzEyMw%3D%3D%0A

  • 负荷被签名

1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b

  • 浏览器重定向至:

http://discuss.example.com/session/sso_login?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z%0AYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl%0Acm5hbF9pZD1oZWxsbzEyMw%3D%3D%0A&sig=1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b

同步 SSO 记录

您可以使用 POST 管理 endpoint /admin/users/sync_sso 来同步一条 SSO 记录,将传给 SSO endpoint 的值传递过去,不用考虑 nonce 了。

登出用户

您可以使用 POST 管理 endpoint /admin/users/{USER_ID}/logout to log out any user in the system if needed.

将来的计划

  • 我们将给出更多平台的 SSO 登录参考实现。如果您实现了,请在官方论坛的 Extensibility / SSO 分类发个帖子。

  • 增加会话过期和/或重新验证逻辑,这样用户不需要总是登录。

  • 考虑增加一个 discourse_sso gem,这样用 Ruby 更好实现。

高级特性


关于SSO单点登录验证的问题
30 分钟内在云上部署 Discourse
请问可以给discourse论坛的每一个板块绑定一个域名吗?
单点登录的加密方式是不是升级了
请问discourse支持oauth、openid和ldap吗
管理员操作指南
Discourse 同步登录问题
Discourse 同步登录问题
(Erick Guan) #2

I moved 5 posts to a new topic: enter 进入容器失败


(xxcxy) #3

我设置的 secret 是 d836444a9e4084d5b224a60c208dce14
但收到的请求是下面这个:
sso=bm9uY2U9NTk1MjhmOTU1ZTgzZjE2Mjc2YjllMzNkYjI0ZTFkM2EmcmV0dXJu%0AX3Nzb191cmw9aHR0cCUzQSUyRiUyRnRlc3RpbmcuY29tbXVuaXR5LnJ1ZmYu%0AaW8lMkZzZXNzaW9uJTJGc3NvX2xvZ2lu%0A&sig=3373331bca23b0c37bb75ba187d8eb7e4aeab6eb3ce9f29aa13e65d096e578af

我对 sso 做 hash 得到的是
7899e8a30cfb336caaf0b0ff898d0c447118a0a789fe3a492e7b29a9c06cfcf8 与 传过来的不符,我该怎么查这个问题?


(xxcxy) #4

sso 登出:/admin/users/{USER_ID}/log_out
文档里少了 下划线


(DaBin) #5

应该是你的nonce问题,nonce要用discourse发来的那个


(小白) #6

误启动后,执行这个命令,貌似不对呢
image

要怎么设置啊,现在无法访问管理面板了,谢谢


(小白) #7

搞定了,在英文官网论坛找到方法了
Disable SSO Through the Discourse Console:

cd /var/discourse
./launcher enter app
rails c
SiteSetting.enable_sso=false
exit
exit