zoukankan      html  css  js  c++  java
  • 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认证?

  • 相关阅读:
    MD5工具类
    新搭建mysql容易出现问题
    docker自动化部署
    k8s入门案例
    docker单一部署jenkins
    Sentinel 学习-简介
    mysql innerjoin,leftjoin,group by,having
    批量更新 分割list 多线程处理
    redis 3.0+ cluser 集群搭建
    ELK 和 Logstach
  • 原文地址:https://www.cnblogs.com/dapianzi/p/7815978.html
Copyright © 2011-2022 走看看