CAS客戶端整合(二) Zabbix


Zabbix是一個強大的服務器/交換機監控應用,有zabbix-server, zabbix-client, zabbix-web 三部分。zabbix-web管理端是用php寫的。
前文參考:CAS客戶端整合(一) Discuz!

登錄流程

修改代碼前例行先確定登錄流程

原登錄過程

Zabbix的登錄流程跟 Discuz 類似。系統從 cookie/session 中讀取用戶會話id,如果用戶為 guest ,只提供部分頁面訪問。需要訪問受限資源,需要再登錄

CAS登錄過程

Zabbix通過cas登錄也跟Discuz幾乎一樣,不過這次保留了zabbix的cookie判斷

代碼修改

引入cas-php-client

CAS客戶端路徑/local/cas,新建/local/cas/CasClient.php:

phpCAS::client ( CAS_VERSION_2_0, CAS_SERVER_HOST, CAS_SERVER_PORT, CAS_SERVER_PATH );

// no SSL validation for the CAS server
phpCAS::setNoCasServerValidation ();
phpCAS::handleLogoutRequests();
/**
 * 關閉url攜帶ticket參數,防止重復認證導致 ticket not recognized
 * @see [https://www.cnblogs.com/next-door-boy/p/3372522.html]
 */
phpCAS::$_PHPCAS_CLIENT->setNoClearTicketsFromUrl ();

/include/config.inc.php中導入

// cas client
require_once dirname(__FILE__).'/../local/cas/CasClient.php';
// zabbix 核心
require_once dirname(__FILE__).'/classes/core/Z.php';

Zabbix 登錄

Zabbix的用戶登錄驗證統一接口/include/class/user/CWebUser::checkAuthentication.這里是主要的登錄邏輯。zabbix從cookie中獲取zbxsessionid,然后比對是否有效sessionid。如果是有效用戶我們直接允許。如果是guest用戶或沒有 sessionid 則檢查cas是否已登錄,如果已登錄則初始化用戶信息(代碼在下一步介紹)。

public static function checkAuthentication($sessionId) {
	try {
		if ($sessionId !== null) {
			self::$data = API::User()->checkAuthentication([$sessionId]);
			/*
			 * login as guest
			 * cas authentication.
			 */
			if (self::$data['alias'] == ZBX_GUEST_USER && phpCAS::isAuthenticated()) {
				$sessionId = login_via_cas(phpCAS::getUser());
				self::setSessionCookie($sessionId);
				return $sessionId;
			}
		}

		if ($sessionId === null || empty(self::$data)) {
			/*
			 * cas authentication.
			 */
			if (phpCAS::isAuthenticated()) {
				$sessionId = login_via_cas(phpCAS::getUser());
				self::setSessionCookie($sessionId);
				return $sessionId;
			}
			self::setDefault();
			self::$data = API::User()->login([
				'user' => ZBX_GUEST_USER,
				'password' => '',
				'userData' => true
			]);

			if (empty(self::$data)) {
				clear_messages(1);
				throw new Exception();
			}
			$sessionId = self::$data['sessionid'];
		}

		if (self::$data['gui_access'] == GROUP_GUI_ACCESS_DISABLED) {
			throw new Exception();
		}

		self::setSessionCookie($sessionId);

		return $sessionId;
	}
	catch (Exception $e) {
		self::setDefault();
		return false;
	}
}

初始化用戶信息

根據用戶名初始化用戶信息,代碼從/include/class/api/services/CUser::login() 方法修改而來

	/**
 * Login from cas
 * init user info.
 * @param $name
 * @author Carl
 */
function login_via_cas($name) {
	$userInfo = DBfetch(DBselect(
		'SELECT u.userid,u.attempt_failed,u.attempt_clock,u.attempt_ip'.
		' FROM users u'.
		' WHERE u.alias='.zbx_dbstr($name)
	));
	if (!$userInfo) {
		//CUser::exception(ZBX_API_ERROR_PARAMETERS, _('Login name or password is incorrect.'));
	}
	// check if user is blocked
	if ($userInfo['attempt_failed'] >= ZBX_LOGIN_ATTEMPTS) {
		if ((time() - $userInfo['attempt_clock']) < ZBX_LOGIN_BLOCK) {
			//CUser::exception(ZBX_API_ERROR_PARAMETERS, _s('Account is blocked for %s seconds', (ZBX_LOGIN_BLOCK - (time() - $userInfo['attempt_clock']))));
		}
		DBexecute('UPDATE users SET attempt_clock='.time().' WHERE alias='.zbx_dbstr($name));
	}
	// check system permissions
	if (!check_perm2system($userInfo['userid'])) {
		//CUser::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions for system access.'));
	}
	$dbAccess = DBfetch(DBselect(
		'SELECT MAX(g.gui_access) AS gui_access'.
		' FROM usrgrp g,users_groups ug'.
		' WHERE ug.userid='.zbx_dbstr($userInfo['userid']).
		' AND g.usrgrpid=ug.usrgrpid'
	));

	if (zbx_empty($dbAccess['gui_access'])) {
		$guiAccess = GROUP_GUI_ACCESS_SYSTEM;
	}
	else {
		$guiAccess = $dbAccess['gui_access'];
	}
	// start session
	$sessionid = md5(time().$name.rand(0, 10000000));
	DBexecute('INSERT INTO sessions (sessionid,userid,lastaccess,status)'.
		' VALUES ('.zbx_dbstr($sessionid).','.zbx_dbstr($userInfo['userid']).','.time().','.ZBX_SESSION_ACTIVE.')'
	);
	$userid = $userInfo['userid'];
	$userData = DBfetch(DBselect(
		'SELECT u.userid,u.alias,u.name,u.surname,u.url,u.autologin,u.autologout,u.lang,u.refresh,u.type,'.
		' u.theme,u.attempt_failed,u.attempt_ip,u.attempt_clock,u.rows_per_page'.
		' FROM users u'.
		' WHERE u.userid='.zbx_dbstr($userid)
	));

	$userData['debug_mode'] = (bool) DBfetch(DBselect(
		'SELECT ug.userid'.
		' FROM usrgrp g,users_groups ug'.
		' WHERE ug.userid='.zbx_dbstr($userid).
		' AND g.usrgrpid=ug.usrgrpid'.
		' AND g.debug_mode='.GROUP_DEBUG_MODE_ENABLED
	));

	$userData['userip'] = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'])
		? $_SERVER['HTTP_X_FORWARDED_FOR']
		: $_SERVER['REMOTE_ADDR'];
	$userData['sessionid'] = $sessionid;
	$userData['gui_access'] = $guiAccess;

	if ($userInfo['attempt_failed']) {
		DBexecute('UPDATE users SET attempt_failed=0 WHERE userid='.zbx_dbstr($userInfo['userid']));
	}
	CWebUser::$data = CUser::$userData = $userData;
	return $sessionid;
}

登出

分析代碼發現zabbix的登出全部轉到 index.php 中處理。修改logout這一段

// VAR	TYPE	OPTIONAL	FLAGS	VALIDATION	EXCEPTION
$fields = [
	'name' =>		[T_ZBX_STR, O_NO,	null,	null,		'isset({enter})', _('Username')],
	'password' =>	[T_ZBX_STR, O_OPT, null,	null,			'isset({enter})'],
	'sessionid' =>	[T_ZBX_STR, O_OPT, null,	null,			null],
	'reconnect' =>	[T_ZBX_INT, O_OPT, P_SYS|P_ACT,	BETWEEN(0, 65535), null],
	'enter' =>		[T_ZBX_STR, O_OPT, P_SYS,	null,			null],
	'autologin' =>	[T_ZBX_INT, O_OPT, null,	null,			null],
	'request' =>	[T_ZBX_STR, O_OPT, null,	null,			null],
    // zabbix 獨特的檢查機制,如果不指定無法從 $_REQUEST 變量獲取獲取請求參數
	'logoutRequest' =>	[T_ZBX_STR, O_OPT, null,	null,			null],
];
check_fields($fields);

// logout
if (isset($_REQUEST['reconnect']) || !empty($_REQUEST['logoutRequest'])) {
	DBstart();
	add_audit_details(AUDIT_ACTION_LOGOUT, AUDIT_RESOURCE_USER, CWebUser::$data['userid'], '', _('Manual Logout'),
		CWebUser::$data['userid']
	);
	DBend(true);
	CWebUser::logout();
	//$ref = empty($_SERVER['HTTP_REFERER']) ? ('http://'.$_SERVER['SERVER_NAME'].substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/')+1).'index.php') : $_SERVER['HTTP_REFERER'];
    // cas 退出后返回的地址
	$ref = 'http://'.$_SERVER['SERVER_NAME'].substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/')+1).'index.php';
	phpCAS::logoutWithRedirectService($ref);
	redirect('index.php');
}

小結

代碼看起來很少,但是要仔細查看源碼分析登錄過程,然后在什么地方做修改還是比較費心思的。
這個流程同步登錄已經沒問題了,但是不能同步登出。
因為zabbix的session是以cookie的形式存儲。而cas-server無法清除客戶端本地的cookie,這里我們又直接根據cookie驗證用戶,所以跳過了cas的狀態復查。
這么看來,如果換成cas認證,那么必須拋棄cookie認證?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM