排版已吐 折腾了好久 踩了好多坑 也很有意思 当然还有写的有问题的地方需要指正
首先要介绍的是 discuz 的 ucenter 所提供的应用同步原理,我们通过 rails 调用一个登陆接口,这个接口返回用户信息,再以用户 id 为参数,调用同步的接口,这个接口返回一段 js,最后渲染这段 js 来操作浏览器设置 discuz 的 cookie 完成同步,登出同理。根据原理实现方式不尽相同,下面给出一种。
写了一个 docker-compose 来快速搭建https://github.com/terryshi96/docker-for-discuz 源码采用 volume 的方式,方便修改。事实上把 html 里面源码换掉就能起其他 php 程序,然后最好是把 docker 镜像源换为国内的,容器内 apt-get 镜像源也换为国内的。
添加加一个别人写的 gem ucenter,提供了与 UCenter api 通信的方法 https://github.com/MgaMPKAy/ucenter 这里直接在 gemfile 里面加 ucenter 将用的是另一个包,需要用 git 的方式添加
添加初始化配置 initializers/ucenter.rb 主要提供 4 个参数 appid 是在 ucenter 中配置的应用 id,编码方式,通信用的 api_url,ucenter 中配置的应用的 key
UCenter.configure do |config|
config.appid = UcenterSetting.appid
config.charset = UcenterSetting.charset
config.api = UcenterSetting.api
config.key = UcenterSetting.key
end
这里我是把配置抽离出来的,也可以直接在初始化脚本里写参数。
appid: 2
charset: gbk
api: http://bbs.xxx.com/uc_server
key: 'xcxcxcx'
bbs_url: http://bbs.xxx.com
ucenter 的配置 配置之后必然会提醒通信失败,这个不管他。
添加核心文件 app/controllers/ucenter_controller.rb
class UcenterController < ApplicationController
skip_before_action :authenticate_user!, only: [:syn_out]
#同步登陆
def sso
#连接ucenter
uc = UCenter.connect
#检查用户是否存在,不存在注册一个,注册主要用到3个参数 用户名 密码 邮箱,这个自己定
if uc.user_checkname(current_user.realname)
ucsuer_register current_user.realname, current_user.created_at, current_user.email
end
#调用登录接口 用一个变量保存登录后的返回值
login_res = uc.user_login current_user.realname, current_user.created_at
#通过返回值中的id调用同步接口
syn_res = uc.user_synlogin login_res[:id]
href = <<HREF
<script language="javascript" type="text/javascript">
function goto_bbs() {
window.location.href = "#{UcenterSetting.bbs_url}"
}
setTimeout('goto_bbs()', 500);
</script>
HREF
#把返回的js渲染到html 同时加上一段跳转js
render html: "#{syn_res}#{href}".html_safe
end
#同步登出 原理与登录相同
def syn_out
if current_user?
uc = UCenter.connect
logout_res = uc.get_user current_user.realname
syn_res = uc.user_synlogout logout_res[:id]
href = <<HREF
<script language="javascript" type="text/javascript">
function goto_bbs() {
window.location.href = "#{HostSetting.site}/users/sign_out"
}
setTimeout('goto_bbs()', 500);
</script>
HREF
render html: "#{syn_res}#{href}".html_safe
else
redirect_to root_path
end
end
end
get 'ucenter/sso' => 'ucenter#sso'
get 'ucenter/syn_out' => 'ucenter#syn_out'
$this->db->query("INSERT INTO `discuz`.pre_common_member SET uid='$uid', username='$username', password='$password', email='$email', adminid='0', groupid='10', regdate='".$this->base->time."', credits='0', timeoffset='9999'");
$this->db->query("INSERT INTO `discuz`.pre_common_member_status SET uid='$uid', regip='$regip', lastip='$regip', lastvisit='".$this->base->time."', lastactivity='".$this->base->time."', lastpost='0', lastsendmail='0'");
$this->db->query("INSERT INTO `discuz`.pre_common_member_profile SET uid='$uid'");
$this->db->query("INSERT INTO `discuz`.pre_common_member_field_forum SET uid='$uid'");
$this->db->query("INSERT INTO `discuz`.pre_common_member_field_home SET uid='$uid'");
$this->db->query("INSERT INTO `discuz`.pre_common_member_count SET uid='$uid', extcredits1='0', extcredits2='0', extcredits3='0', extcredits4='0', extcredits5='0', extcredits6='0', extcredits7='0', extcredits8='0'");
if(empty($_G['uid'])) {
header("location: http://bbs.xxx.com/bbs_sign_in");
exit;
}
class BbsSessionsController < Devise::SessionsController
respond_to :html, :js
include Concerns::CommonShare
def new
super
end
def after_sign_in_path_for(resource)
ucenter_sso_path
end
end
get 'bbs_sign_in' => 'bbs_sessions#new'
关于判断登录后跳转地址也可以这样写,意思是如果是来自 bbs_sign_in 的登录请求,返回一个跳转地址是 ucenter/sso。或者其他你们自己的判断登录跳转的方式。
if request.referrer == "#{HostSetting.site}#{bbs_sign_in_path}"
return ucenter_sso_path
end
// --------------- 登录页 -----------//
#users_sessions_new, #bbs_sessions_new //--<- 新增的--//
至此,访问论坛首页,会判断是否登录,未登录会跳转到 主站/bbs_sign_in 页,如果主站未登录将显示,主站登录页,登录后将跳转到/ucenter/sso,执行同步登录操作,之后跳转回论坛并且为登录状态;主站已登录则会直接跳转到/ucenter/sso 进行同步登录。
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :get
把所有退出按钮对应的 link_to 改为 ucenter_syn_out_path, method: :get
把 discuz 中的退出链接改为http://bbs.xxx.com/ucenter/syn_out 主要是这两个文件
discuz/template/default/search/header.htm
discuz/template/default/common/header_userstatus.htm
至此,两站的退出全部指向 bbs.xxx.com/ucenter/syn_out,然后这个 action(代码见上文) 会先对论坛进行同步退出操作,之后跳转回主站/users/sign_out,将主站退出,最后跳转到主站退出页面。 最后,不推荐集成 discuz,这个也只是提供一个集成论坛的思路。
这个比集成 discuz 少踩了一些坑~~
ln -s /usr/bin/docker /usr/bin/docker.io
./discourse-setup
之后生成 container/app.yml 这个配置文件,需要对其修改
- "templates/web.china.template.yml" # <-- Added 这样gem源就为ruby-china
- "templates/web.socketed.template.yml"# <-- Added 用于配置nignx
# - "80:80"
# - "443:443"
DISCOURSE_SMTP_AUTHENTICATION: login
DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: none
DISCOURSE_SMTP_ENABLE_START_TLS: false
DISCOURSE_SMTP_ADDRESS: smtpcloud.sohu.com
DISCOURSE_SMTP_PORT: 25
DISCOURSE_SMTP_USER_NAME: your_account
DISCOURSE_SMTP_PASSWORD: "your_password"
邮件的配置会影响到能否发送邮件,而且管理员账户需要通过邮件激活。当然也能手动创建管理员。
cd /var/discourse (你的discourse目录)
./launcher enter app
rake admin:create
server {
listen 80; listen [::]:80;
server_name forum.example.com; # <-- change this
location / {
proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
} }
./launcher start app
discourse 需要的配置,可以直接在管理界面搜索关键字,就能显示相应配置。这里我们配置了 API KEY,启用 sso 并添加 sso secret 和 sso url,修改登出跳转 url。
gem 'discourse_api'
secret: 'xxxxxxxxx'
host: http://bbs.xxx.com
sso_url: http://bbs.xxx.com/session/sso_login
api_key: xxxxxxxxxx
admin_email: [email protected]
class DiscourseSsoController < ApplicationController
def sso
if current_user
secret = DiscourseSetting.secret
#解析sso登录请求
sso = DiscourseApi::SingleSignOn.parse(request.query_string, secret)
#判断是否是管理员
if DiscourseSetting.admin_email == current_user.email
sso.admin = true
end
#登录用户信息设置
sso.email = current_user.email
sso.name = current_user.realname
str = current_user.email
sso.username = "#{str.split(pattern='@').first}_#{current_user.id}"
sso.external_id = current_user.id
sso.sso_secret = secret
redirect_to sso.to_url(DiscourseSetting.sso_url)
else
#如果主站未登录,跳转到登录页
redirect_to bbs_sign_in_path
end
end
end
get 'discourse/sso' => 'discourse_sso#sso'
class BbsSessionsController < Devise::SessionsController
respond_to :html, :js
include Concerns::CommonShare
def new
super
end
def destroy
#删除session操作
super
end
end
get 'bbs_sign_in' => 'bbs_sessions#new'
get 'bbs_sign_out' => 'bbs_sessions#destroy'
其中 bbs_sign_in 是论坛所使用的登录页,需要单独为这个登录添加登录跳转,跳转回论坛
if request.referrer == "#{HostSetting.site}#{bbs_sign_in_path}"
return DiscourseSetting.host
end
bbs_sign_out 是论坛退出后的跳转地址,即论坛退出后操作主站也退出
至此,访问 discourse 会跳转到主站/discourse/sso 请求 sso 登录,如果主站已登录则会同步论坛登录并跳转回论坛;如果主站未登录则会跳转到/bbs_sign_in 这个论坛登录入口,登陆后跳转到论坛页面,discourse 重新执行 sso 登录。然后再论坛端退出,会先退出论坛然后跳转主站/bbs_sign_out 同步主站退出。
def destroy
begin
client = DiscourseApi::Client.new(DiscourseSetting.host)
client.api_key = DiscourseSetting.api_key
str = current_user.email
client.api_username = "#{str.split(pattern='@').first}_#{current_user.id}"
user = client.by_external_id(current_user.id)
client.log_out(user["id"]) if user
ensure
#删除sessions操作
super
end
end
至此集成 Discourse 算全部结束 撒花