CAS客户端整合 Zabbix

Posted Dapianzi卡夫卡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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认证?

以上是关于CAS客户端整合 Zabbix的主要内容,如果未能解决你的问题,请参考以下文章

cas4.2.7与shiro进行整合

markdown [Apereo CAS 3.5 CORE] Apereo CAS 3.5 #CAS的核心代码片段

Cas 服务器 下载编译及部署

cas与NGINX整合(转)

Spring Security 整合Cas

Spring Security(20)——整合Cas