Discuz 3.X 整合 CAS 的方法
有很多朋友问我为什么没能整合成功?
我也没有能及时回复,在此表示抱歉。
实际上将登录的过程是在Cas Server 登录页面 中完成的, DZ中的登录用户名密码这些都可以隐藏掉。 这个需要改模板了。
1,点登录按钮,跳到CAS。
2,在CAS 登录完成后,跳回Dz,这个回调的Url会有一个ticket 。
3,DZ会根据这个ticket,通过后台去CAS Server验证这个ticket是否有效。
4,如果有效就置用户为登录状态。
其实没有整合成功是很正常的,因为我使用Server不是Java版的,是我自已重新在原有业务系统上根据CAS协议做的。与标准的有一点点差别。
下面是13年写的,只做参考即可。原理方面的东西是一致的。
没有整合成功的:
1,注意一下这个,这个是关键
phpCAS::getAttribute ...
在CasServer 返回回应中(上面说的过程中第三步) 中可以设置 attribute ,Cas Server , (java 版的在读代码的时候,见到过类似的设置)
代码中用到的
$email = phpCAS::getAttribute('Email');
$uid = phpCAS::getAttribute('Uid');
这个email , uid 都是 cas server 自定义的。其实你的返回的并一定有这几,你也可以根据自己的业务情况,自定其它的返回,只要能标记用户就行了。
2, userloginCas 这个方法是我自己的写的,
它的功能就是把上面返回的数据,进行加工,让DZ ”以为“ 数据是真的。
————————————————————————————————————2015-11-24 更新————————————————————————————————————————————
1,新建 CasClient.php
<?php
include_once (dirname ( __FILE__ ) . '/CasClientConfig.php'); // 注意这个
include_once (dirname ( __FILE__ ) . '/CAS.php'); // 注意这个
// 初始化
phpCAS::setDebug ();
// initialize phpCAS
phpCAS::client ( CAS_VERSION_2_0, CAS_SERVER_HOSTNAME, CAS_SERVER_PORT, CAS_SERVER_APP_NAME );
// no SSL validation for the CAS server
phpCAS::setNoCasServerValidation ();
2,新建 CasClientConfig.php
// define ( 'CAS_SERVER_HOSTNAME', 'caslocal.youxituan.com' );
// define ( 'CAS_SERVER_PORT', 80 );
define ( 'CAS_SERVER_HOSTNAME', 'localhost' );
define ( 'CAS_SERVER_PORT', 34382 );
define ( 'CAS_SERVER_APP_NAME', "cas" );
define ( 'LocalLoginPath', '/cas/localogin.php' );
define ( 'LocalCheckPath', '/cas/logincheck.php' );
define ( 'LocalLogOffPath', '/cas/logoff.php' );
3, 修改 class_core.php
error_reporting(E_ALL);
define('IN_DISCUZ', true);
define('DISCUZ_ROOT', substr(dirname(__FILE__), 0, -12));
define('DISCUZ_CORE_DEBUG', false);
define('DISCUZ_TABLE_EXTENDABLE', false);
require_once DISCUZ_ROOT."Cas/CasClient.php";
set_exception_handler(array('core', 'handleException'));
if(DISCUZ_CORE_DEBUG) {
3,修改 function_member.php 支持 CAS 登录
增加一个方法
// 新加的方法,用以支持CAS 登录
function userloginCas($username, $password, $email, $uid) {
$return = array ();
$merge = 0;
$status = $uid;
$mockcallback = array($uid, $username, $password, $email, $merge);
$return ['ucresult'] = $mockcallback;
// $return ['ucresult'] ['status'] = 0;
// $return ['ucresult'] ['username'] = $username;
// $return ['ucresult'] ['password'] = $password;
// $return ['ucresult'] ['email'] = $email;
// $return ['ucresult'] ['uid'] = $uid;
// $return ['ucresult'] ['merge'] = $merge;
// }
$tmp = array ();
$duplicate = '';
list ( $tmp ['uid'], $tmp ['username'], $tmp ['password'], $tmp ['email'], $duplicate ) = $return ['ucresult'];
$return ['ucresult'] = $tmp;
if ($duplicate && $return ['ucresult'] ['uid'] > 0 || $return ['ucresult'] ['uid'] <= 0) {
$return ['status'] = 0;
return $return;
}
$member = getuserbyuid ( $return ['ucresult'] ['uid'], 1 );
if (! $member || empty ( $member ['uid'] )) {
$return ['status'] = - 1;
return $return;
}
$return ['member'] = $member;
$return ['status'] = 1;
if ($member ['_inarchive']) {
C::t ( 'common_member_archive' )->move_to_master ( $member ['uid'] );
}
if ($member ['email'] != $return ['ucresult'] ['email']) {
C::t ( 'common_member' )->update ( $return ['ucresult'] ['uid'], array (
'email' => $return ['ucresult'] ['email']
) );
}
return $return;
}
4,修改 class_member.php 添加支持 Cas登录支持
修改 OnLogin 方法
function on_login() {
global $_G;
if ($_G ['uid']) {
$referer = dreferer ();
$ucsynlogin = $this->setting ['allowsynlogin'] ? uc_user_synlogin ( $_G ['uid'] ) : '';
$param = array (
'username' => $_G ['member'] ['username'],
'usergroup' => $_G ['group'] ['grouptitle'],
'uid' => $_G ['member'] ['uid']
);
showmessage ( 'login_succeed', $referer ? $referer : './', $param, array (
'showdialog' => 1,
'locationtime' => true,
'extrajs' => $ucsynlogin
) );
}
list ( $seccodecheck ) = seccheck ( 'login' );
if (! empty ( $_GET ['auth'] )) {
$dauth = authcode ( $_GET ['auth'], 'DECODE', $_G ['config'] ['security'] ['authkey'] );
list ( , , , $secchecklogin2 ) = explode ( "\t", $dauth );
if ($secchecklogin2) {
$seccodecheck = true;
}
}
$seccodestatus = ! empty ( $_GET ['lssubmit'] ) ? false : $seccodecheck;
$invite = getinvite ();
// if(!submitcheck('loginsubmit', 1, $seccodestatus)) {
if (1 == 2) {
$auth = '';
$username = ! empty ( $_G ['cookie'] ['loginuser'] ) ? dhtmlspecialchars ( $_G ['cookie'] ['loginuser'] ) : '';
if (! empty ( $_GET ['auth'] )) {
list ( $username, $password, $questionexist ) = explode ( "\t", authcode ( $_GET ['auth'], 'DECODE', $_G ['config'] ['security'] ['authkey'] ) );
$username = dhtmlspecialchars ( $username );
$auth = dhtmlspecialchars ( $_GET ['auth'] );
}
$cookietimecheck = ! empty ( $_G ['cookie'] ['cookietime'] ) || ! empty ( $_GET ['cookietime'] ) ? 'checked="checked"' : '';
if ($seccodecheck) {
$seccode = random ( 6, 1 ) + $seccode {0} * 1000000;
}
if ($this->extrafile && file_exists ( $this->extrafile )) {
require_once $this->extrafile;
}
$navtitle = lang ( 'core', 'title_login' );
include template ( $this->template );
} else {
if (! empty ( $_GET ['auth'] )) {
list ( $_GET ['username'], $_GET ['password'] ) = daddslashes ( explode ( "\t", authcode ( $_GET ['auth'], 'DECODE', $_G ['config'] ['security'] ['authkey'] ) ) );
}
$loginhash = ! empty ( $_GET ['loginhash'] ) && preg_match ( '/^\w+$/', $_GET ['loginhash'] ) ? $_GET ['loginhash'] : '';
if (! ($_G ['member_loginperm'] = logincheck ( $_GET ['username'] ))) {
captcha::report ( $_G ['clientip'] );
showmessage ( 'login_strike' );
}
if ($_GET ['fastloginfield']) {
$_GET ['loginfield'] = $_GET ['fastloginfield'];
}
$_G ['uid'] = $_G ['member'] ['uid'] = 0;
$_G ['username'] = $_G ['member'] ['username'] = $_G ['member'] ['password'] = '';
phpCAS::$_PHPCAS_CLIENT->setNoClearTicketsFromUrl ();
$auth1 = phpCAS::isAuthenticated ();
$username='';
if ($auth1) {
$username = phpCAS::getUser ();
} else {
phpCAS::forceAuthentication ();
// showmessage ( '尚未登录,<a href="" onclick="return gocas();">前去登录</a>' );
}
$password = '';
$email = phpCAS::getAttribute('Email');
$uid = phpCAS::getAttribute('Uid');
// $result = userlogin($_GET['username'], $_GET['password'], $_GET['questionid'], $_GET['answer'], $this->setting['autoidselect'] ? 'auto' : $_GET['loginfield'], $_G['clientip']);
$result = userloginCas ( $username, $password, $email, $uid );
$uid = $result ['ucresult'] ['uid'];
if (! empty ( $_GET ['lssubmit'] ) && ($result ['ucresult'] ['uid'] == - 3 || $seccodecheck)) {
$_GET ['username'] = $result ['ucresult'] ['username'];
$this->logging_more ( $result ['ucresult'] ['uid'] == - 3 );
}
if ($result ['status'] == - 1) {
if (! $this->setting ['fastactivation']) {
$auth = authcode ( $result ['ucresult'] ['username'] . "\t" . FORMHASH, 'ENCODE' );
showmessage ( 'location_activation', 'member.php?mod=' . $this->setting ['regname'] . '&action=activation&auth=' . rawurlencode ( $auth ) . '&referer=' . rawurlencode ( dreferer () ), array (), array (
'location' => true
) );
} else {
$init_arr = explode ( ',', $this->setting ['initcredits'] );
$groupid = $this->setting ['regverify'] ? 8 : $this->setting ['newusergroupid'];
C::t ( 'common_member' )->insert ( $uid, $result ['ucresult'] ['username'], md5 ( random ( 10 ) ), $result ['ucresult'] ['email'], $_G ['clientip'], $groupid, $init_arr );
$result ['member'] = getuserbyuid ( $uid );
$result ['status'] = 1;
}
}
if ($result ['status'] > 0) {
if ($this->extrafile && file_exists ( $this->extrafile )) {
require_once $this->extrafile;
}
setloginstatus ( $result ['member'], $_GET ['cookietime'] ? 2592000 : 0 );
checkfollowfeed ();
if ($_G ['group'] ['forcelogin']) {
if ($_G ['group'] ['forcelogin'] == 1) {
clearcookies ();
showmessage ( 'location_login_force_qq' );
} elseif ($_G ['group'] ['forcelogin'] == 2 && $_GET ['loginfield'] != 'email') {
clearcookies ();
showmessage ( 'location_login_force_mail' );
}
}
if ($_G ['member'] ['lastip'] && $_G ['member'] ['lastvisit']) {
dsetcookie ( 'lip', $_G ['member'] ['lastip'] . ',' . $_G ['member'] ['lastvisit'] );
}
C::t ( 'common_member_status' )->update ( $_G ['uid'], array (
'lastip' => $_G ['clientip'],
'port' => $_G ['remoteport'],
'lastvisit' => TIMESTAMP,
'lastactivity' => TIMESTAMP
) );
$ucsynlogin = $this->setting ['allowsynlogin'] ? uc_user_synlogin ( $_G ['uid'] ) : '';
$pwold = false;
if ($this->setting ['strongpw'] && ! $this->setting ['pwdsafety']) {
if (in_array ( 1, $this->setting ['strongpw'] ) && ! preg_match ( "/\d+/", $_GET ['password'] )) {
$pwold = true;
}
if (in_array ( 2, $this->setting ['strongpw'] ) && ! preg_match ( "/[a-z]+/", $_GET ['password'] )) {
$pwold = true;
}
if (in_array ( 3, $this->setting ['strongpw'] ) && ! preg_match ( "/[A-Z]+/", $_GET ['password'] )) {
$pwold = true;
}
if (in_array ( 4, $this->setting ['strongpw'] ) && ! preg_match ( "/[^a-zA-z0-9]+/", $_GET ['password'] )) {
$pwold = true;
}
}
if ($_G ['member'] ['adminid'] != 1) {
if ($this->setting ['accountguard'] ['loginoutofdate'] && $_G ['member'] ['lastvisit'] && TIMESTAMP - $_G ['member'] ['lastvisit'] > 90 * 86400) {
C::t ( 'common_member' )->update ( $_G ['uid'], array (
'freeze' => 2
) );
C::t ( 'common_member_validate' )->insert ( array (
'uid' => $_G ['uid'],
'submitdate' => TIMESTAMP,
'moddate' => 0,
'admin' => '',
'submittimes' => 1,
'status' => 0,
'message' => '',
'remark' => ''
), false, true );
manage_addnotify ( 'verifyuser' );
showmessage ( 'location_login_outofdate', 'home.php?mod=spacecp&ac=profile&op=password&resend=1', array (
'type' => 1
), array (
'showdialog' => true,
'striptags' => false,
'locationtime' => true
) );
}
if ($this->setting ['accountguard'] ['loginpwcheck'] && $pwold) {
$freeze = $pwold;
if ($this->setting ['accountguard'] ['loginpwcheck'] == 2 && $freeze) {
C::t ( 'common_member' )->update ( $_G ['uid'], array (
'freeze' => 1
) );
}
}
}
$seccheckrule = & $_G ['setting'] ['seccodedata'] ['rule'] ['login'];
if ($seccheckrule ['allow'] == 2) {
if ($seccheckrule ['nolocal']) {
require_once libfile ( 'function/misc' );
$lastipConvert = process_ipnotice ( convertip ( $_G ['member'] ['lastip'] ) );
$nowipConvert = process_ipnotice ( convertip ( $_G ['clientip'] ) );
if ($lastipConvert != $nowipConvert && stripos ( $lastipConvert, $nowipConvert ) == false && stripos ( $nowipConvert, $lastipConvert ) == false) {
$seccodecheck = true;
}
}
if (! $seccodecheck && $seccheckrule ['pwsimple'] && $pwold) {
$seccodecheck = true;
}
if (! $seccodecheck && $seccheckrule ['outofday'] && $_G ['member'] ['lastvisit'] && TIMESTAMP - $_G ['member'] ['lastvisit'] > $seccheckrule ['outofday'] * 86400) {
$seccodecheck = true;
}
if (! $seccodecheck && $_G ['member_loginperm'] < 4) {
$seccodecheck = true;
}
if (! $seccodecheck && $seccheckrule ['numiptry']) {
$seccodecheck = failedipcheck ( $seccheckrule ['numiptry'], $seccheckrule ['timeiptry'] );
}
if ($seccodecheck && ! $secchecklogin2) {
clearcookies ();
$auth = authcode ( $_GET ['username'] . "\t" . $_GET ['password'] . "\t" . ($_GET ['questionid'] ? 1 : 0) . "\t1", 'ENCODE', $_G ['config'] ['security'] ['authkey'] );
$location = 'member.php?mod=logging&action=login&auth=' . rawurlencode ( $auth ) . '&referer=' . rawurlencode ( dreferer () ) . (! empty ( $_GET ['cookietime'] ) ? '&cookietime=1' : '');
if (defined ( 'IN_MOBILE' )) {
showmessage ( 'login_seccheck2', $location );
} else {
$js = '<script type="text/javascript">location.href=\'' . $location . '\'</script>';
showmessage ( 'login_seccheck2', '', array (
'type' => 1
), array (
'extrajs' => $js
) );
}
}
}
if ($invite ['id']) {
$result = C::t ( 'common_invite' )->count_by_uid_fuid ( $invite ['uid'], $uid );
if (! $result) {
C::t ( 'common_invite' )->update ( $invite ['id'], array (
'fuid' => $uid,
'fusername' => $_G ['username']
) );
updatestat ( 'invite' );
} else {
$invite = array ();
}
}
if ($invite ['uid']) {
require_once libfile ( 'function/friend' );
friend_make ( $invite ['uid'], $invite ['username'], false );
dsetcookie ( 'invite_auth', '' );
if ($invite ['appid']) {
updatestat ( 'appinvite' );
}
}
$param = array (
'username' => $result ['ucresult'] ['username'],
'usergroup' => $_G ['group'] ['grouptitle'],
'uid' => $_G ['member'] ['uid'],
'groupid' => $_G ['groupid'],
'syn' => $ucsynlogin ? 1 : 0
);
$extra = array (
'showdialog' => true,
'locationtime' => true,
'extrajs' => $ucsynlogin
);
if (! $freeze || ! $this->setting ['accountguard'] ['loginpwcheck']) {
$loginmessage = $_G ['groupid'] == 8 ? 'login_succeed_inactive_member' : 'login_succeed';
$location = $invite || $_G ['groupid'] == 8 ? 'home.php?mod=space&do=home' : dreferer ();
} else {
$loginmessage = 'login_succeed_password_change';
$location = 'home.php?mod=spacecp&ac=profile&op=password';
$_GET ['lssubmit'] = 0;
}
if (empty ( $_GET ['handlekey'] ) || ! empty ( $_GET ['lssubmit'] )) {
if (defined ( 'IN_MOBILE' )) {
showmessage ( $loginmessage, $location, $param, array (
'location' => true
) );
} else {
if (! empty ( $_GET ['lssubmit'] )) {
if (! $ucsynlogin) {
$extra ['location'] = true;
}
showmessage ( $loginmessage, $location, $param, $extra );
} else {
$href = str_replace ( "'", "\'", $location );
showmessage ( 'location_login_succeed', $location, array (), array (
'showid' => 'succeedmessage',
'extrajs' => '<script type="text/javascript">' . 'setTimeout("window.location.href =\'' . $href . '\';", 3000);' . '$(\'succeedmessage_href\').href = \'' . $href . '\';' . '$(\'main_message\').style.display = \'none\';' . '$(\'main_succeed\').style.display = \'\';' . '$(\'succeedlocation\').innerHTML = \'' . lang ( 'message', $loginmessage, $param ) . '\';</script>' . $ucsynlogin,
'striptags' => false,
'showdialog' => true
) );
}
}
} else {
showmessage ( $loginmessage, $location, $param, $extra );
}
} else {
$password = preg_replace ( "/^(.{" . round ( strlen ( $_GET ['password'] ) / 4 ) . "})(.+?)(.{" . round ( strlen ( $_GET ['password'] ) / 6 ) . "})$/s", "\\1***\\3", $_GET ['password'] );
$errorlog = dhtmlspecialchars ( TIMESTAMP . "\t" . ($result ['ucresult'] ['username'] ? $result ['ucresult'] ['username'] : $_GET ['username']) . "\t" . $password . "\t" . "Ques #" . intval ( $_GET ['questionid'] ) . "\t" . $_G ['clientip'] );
writelog ( 'illegallog', $errorlog );
loginfailed ( $_GET ['username'] );
failedip ();
$fmsg = $result ['ucresult'] ['uid'] == '-3' ? (empty ( $_GET ['questionid'] ) || $answer == '' ? 'login_question_empty' : 'login_question_invalid') : 'login_invalid';
if ($_G ['member_loginperm'] > 1) {
showmessage ( $fmsg, '', array (
'loginperm' => $_G ['member_loginperm'] - 1
) );
} elseif ($_G ['member_loginperm'] == - 1) {
showmessage ( 'login_password_invalid' );
} else {
showmessage ( 'login_strike' );
}
}
}
}
修改 on_logout 支持同步退出
function on_logout() {
global $_G;
$ucsynlogout = $this->setting ['allowsynlogin'] ? uc_user_synlogout () : '';
if ($_GET ['formhash'] != $_G ['formhash']) {
showmessage ( 'logout_succeed', dreferer (), array (
'formhash' => FORMHASH,
'ucsynlogout' => $ucsynlogout,
'referer' => rawurlencode ( dreferer () )
) );
}
clearcookies ();
$_G ['groupid'] = $_G ['member'] ['groupid'] = 7;
$_G ['uid'] = $_G ['member'] ['uid'] = 0;
$_G ['username'] = $_G ['member'] ['username'] = $_G ['member'] ['password'] = '';
$_G ['setting'] ['styleid'] = $this->setting ['styleid'];
if (defined ( 'IN_MOBILE' )) {
showmessage ( 'location_logout_succeed_mobile', dreferer (), array (
'formhash' => FORMHASH,
'referer' => rawurlencode ( dreferer () )
) );
} else {
// 退出
$service = dreferer () ;
phpCAS::logoutWithRedirectService ( $service );
// 后面这个showmessage提示实际上已经不会执行了
// showmessage ( 'logout_succeed', dreferer (), array (
// 'formhash' => FORMHASH,
// 'ucsynlogout' => $ucsynlogout,
// 'referer' => rawurlencode ( dreferer () )
// ) );
}
}
}
当然,最后把登录改成 到登录界面去登录的方式,这样才会生效,这其实是最简单的方法了。
如下图进行设置
CAS 结构 如下:
其中CAS 包我也做了小小的修改,但这个与主要业务无关,就不传了。
最后,我的版本是 Discuz! X3.1 Release 20131011 。
从 其它系统跳转到要检测是否已经登录,可以作用JS的方法判断CAS Server是否已经登录,如果已经登录,跳转到登录然后再返回
或使得JS方式从后台调用 CAS的方法进行登录,然后使用Ajax的方式,更新界面。
我的CASServer C# 版本 是自己的写的,但我基本上遵循的 CAS 2.0 的协议,所以对于Java版本CAS也差不多。相应代码可参见的GitHub上的代码。