diff --git a/htdocs/lib/Database/DBUserSession.php b/htdocs/lib/Database/DBUserSession.php new file mode 100644 index 0000000..48d85a3 --- /dev/null +++ b/htdocs/lib/Database/DBUserSession.php @@ -0,0 +1,210 @@ + + * @version 2.0.0 + * @package UElearning + * @subpackage Database + */ +class DBUserSession extends Database { + + /** + * 新增登入資料 + * @param string $token 登入token + * @param string $uId 帳號ID + * @param string $agent 登入所使用的裝置 + */ + public function login($token, $uId, $agent) { + + //紀錄登入階段進資料庫 + $sqlString = "INSERT INTO ".$this->table('UserSession'). + " (`UsID`, `UToken`, `UID`, `UAgent`, `ULoginDate`, `ULogoutDate`) + VALUES (NULL , :token, :uid , :agent , NOW() , NULL)"; + + $query = $this->connDB->prepare($sqlString); + $query->bindParam(":token", $token); + $query->bindParam(":uid", $uId); + $query->bindParam(":agent", $agent); + $query->execute(); + } + + /** + * 標注此登入階段為登出 + * @param string $token 登入token + */ + public function logout($token) { + + $sqlString = "UPDATE ".$this->table('UserSession'). + " SET `UToken` = NULL, `ULogoutDate` = NOW() + WHERE `UToken` = :token"; + + $query = $this->connDB->prepare($sqlString); + $query->bindParam(":token", $token); + $query->execute(); + } + + /** + * 標注此帳號所有的登入階段為登出 + * @param string $uid 帳號ID + * @return int 修改幾筆資料 + */ + public function logoutByUserId($uid) { + + $sqlString = "UPDATE ".$this->table('UserSession'). + " SET `UToken` = NULL, `ULogoutDate` = NOW() + WHERE `UID` = :uid AND `UToken` IS NOT NULL"; + + $query = $this->connDB->prepare($sqlString); + $query->bindParam(":uid", $uid); + $query->execute(); + return $query->rowCount(); + } + + /** + * 以token查詢 + * @param string $token 登入token + * @return array 登入階段資料陣列,格式為: + * array( + * 'session_id' => <登入編號>, + * 'token' => <登入Token>, + * 'user_id' => <使用者>, + * 'agent' => <用哪個裝置登入>, + * 'login_date' => <登入時間>, + * 'logout_date' => <登出時間> + * ); + */ + public function queryByToken($token) { + $sqlString = "SELECT * FROM ".$this->table('UserSession'). + " WHERE `UToken` = :token"; + + $query = $this->connDB->prepare($sqlString); + $query->bindParam(':token', $token); + $query->execute(); + + $queryResultAll = $query->fetchAll(); + // 如果有查到一筆以上 + if( count($queryResultAll) >= 1 ) { + $queryResult = $queryResultAll[0]; + + $result = array( + 'session_id' => $queryResult['UsID'], + 'token' => $queryResult['UToken'], + 'user_id' => $queryResult['UID'], + 'agent' => $queryResult['UAgent'], + 'login_date' => $queryResult['ULoginDate'], + 'logout_date' => $queryResult['ULogoutDate'] + ); + + return $result; + } + else return null; + } + + /** + * 以使用者ID查詢 + * @param string $uId 使用者ID + * @return array 登入階段資料陣列,格式為: + * array( + * array( + * 'session_id' => <登入編號>, + * 'token' => <登入Token>, + * 'user_id' => <使用者>, + * 'agent' => <用哪個裝置登入>, + * 'login_date' => <登入時間>, + * 'logout_date' => <登出時間> + * ) + * ); + */ + public function queryByUserId($uId) { + $sqlString = "SELECT * FROM ".$this->table('UserSession'). + " WHERE `UID` = :uid"; + + $query = $this->connDB->prepare($sqlString); + $query->bindParam(':uid', $uId); + $query->execute(); + + $queryResultAll = $query->fetchAll(); + // 如果有查到一筆以上 + if( count($queryResultAll) >= 1 ) { + + // 製作回傳結果陣列 + $result = array(); + foreach($queryResultAll as $key => $thisResult) { + array_push($result, + array( + 'session_id' => $thisResult['UsID'], + 'token' => $thisResult['UToken'], + 'user_id' => $thisResult['UID'], + 'agent' => $thisResult['UAgent'], + 'login_date' => $thisResult['ULoginDate'], + 'logout_date' => $thisResult['ULogoutDate'] + ) + ); + } + return $result; + } + else return null; + } + + /** + * 以使用者ID查詢,目前所有已登入的登入階段 + * @param string $uId 使用者ID + * @return array 登入階段資料陣列,格式為: + * array( + * array( + * 'session_id' => <登入編號>, + * 'token' => <登入Token>, + * 'user_id' => <使用者>, + * 'agent' => <用哪個裝置登入>, + * 'login_date' => <登入時間>, + * 'logout_date' => <登出時間> + * ) + * ); + */ + public function queryLoginByUserId($uId) { + $sqlString = "SELECT * FROM ".$this->table('UserSession'). + " WHERE `UID` = :uid AND `UToken` IS NOT NULL"; + + $query = $this->connDB->prepare($sqlString); + $query->bindParam(':uid', $uId); + $query->execute(); + + $queryResultAll = $query->fetchAll(); + // 如果有查到一筆以上 + if( count($queryResultAll) >= 1 ) { + + // 製作回傳結果陣列 + $result = array(); + foreach($queryResultAll as $key => $thisResult) { + array_push($result, + array( + 'session_id' => $thisResult['UsID'], + 'token' => $thisResult['UToken'], + 'user_id' => $thisResult['UID'], + 'agent' => $thisResult['UAgent'], + 'login_date' => $thisResult['ULoginDate'], + 'logout_date' => $thisResult['ULogoutDate'] + ) + ); + } + return $result; + } + else return null; + } + +} \ No newline at end of file diff --git a/htdocs/lib/User/Exception.php b/htdocs/lib/User/Exception.php index 1359a91..f24ae2e 100644 --- a/htdocs/lib/User/Exception.php +++ b/htdocs/lib/User/Exception.php @@ -238,4 +238,39 @@ class PermissionNoFoundException extends \UnexpectedValueException { public function getName() { return $this->permissionName; } + +} + +// ============================================================================ + +/** + * 找不到此登入階段 + * @since 2.0.0 + * @package UElearning + * @subpackage User + */ +class LoginTokenNoFoundException extends \UnexpectedValueException { + + /** + * 登入階段Token + * @type string + */ + private $token; + + /** + * 找不到此登入階段例外 + * @param string $token 登入階段Token + */ + public function __construct($token) { + $this->token = $token; + parent::__construct('No Found Login Token: '.$this->token); + } + + /** + * 取得輸入的登入階段Token + * @return string 登入階段Token + */ + public function getToken() { + return $this->token; + } } \ No newline at end of file diff --git a/htdocs/lib/User/UserSession.php b/htdocs/lib/User/UserSession.php index 0f66d39..f2f5b46 100644 --- a/htdocs/lib/User/UserSession.php +++ b/htdocs/lib/User/UserSession.php @@ -5,6 +5,13 @@ namespace UElearning\User; +require_once UELEARNING_LIB_ROOT.'/User/User.php'; +require_once UELEARNING_LIB_ROOT.'/User/Exception.php'; +require_once UELEARNING_LIB_ROOT.'/Util/Password.php'; +require_once UELEARNING_LIB_ROOT.'/Database/DBUserSession.php'; +use UElearning\Util; +use UElearning\Database; + /** * 使用者登入階段管理 * @@ -17,45 +24,166 @@ class UserSession { /** * 使用者登入 - * @param string $userId 帳號名稱 - * @param string $password 密碼 - * @return string 登入session token - * @since 2.0.0 + * + * 範例: + * + * try { + * $session = new User\UserSession(); + * echo 'Token: '$session->login('yuan', '123456', 'browser'); + * } + * catch (User\Exception\UserNoFoundException $e) { + * echo 'No Found user: '. $e->getUserId(); + * } + * catch (User\Exception\UserPasswordErrException $e) { + * echo 'User Password wrong: '. $e->getUserId(); + * } + * catch (User\Exception\UserNoActivatedException $e) { + * echo 'User No Activiated: '. $e->getUserId(); + * } + * + * @param string $userId 帳號名稱 + * @param string $password 密碼 + * @param string $agent 用什麼裝置登入 + * @return string 登入session token + * @throw UElearning\User\Exception\UserNoFoundException + * @throw UElearning\User\Exception\UserPasswordErrException + * @throw UElearning\User\Exception\UserNoActivatedException + * @since 2.0.0 */ - public function login($userId, $password) { - // TODO: Fill code in - // 如果登入錯誤,就丟例外 + public function login($userId, $password, $agent) { + + try { + $user = new User($userId); + + // 登入密碼錯誤的話 + if( !$user->isPasswordCorrect($password) ) { + throw new Exception\UserPasswordErrException($userId); + } + // 此帳號已被停用 + else if( !$user->isEnable() ) { + throw new Exception\UserNoActivatedException($userId); + } + // 沒問題,登入此帳號 + else { + + // 使用資料庫 + $db = new Database\DBUserSession(); + + // 產生登入token + $passUtil = new Util\Password(); + $token = null; + // 防止產生出重複的token + do { + $token = $passUtil->generator(32); + } + while ($db->queryByToken($token)); + + // 登入資訊寫入資料庫 + $db->login($token, $userId, $agent); + + return $token; + } + } + // 沒有找到使用者 + catch (User\Exception\UserNoFoundException $e) { + echo 'No Found user: '. $e->getUserId(); + } } // ======================================================================== /** * 使用者登出 + * + * 範例: + * + * try { + * $session = new User\UserSession(); + * $session->logout('YdcfzqUuuRAR]4h6u4^Ew-qa4A-kvD5C'); + * } + * catch (User\Exception\LoginTokenNoFoundException $e) { + * echo 'No Login by token: '. $e->getToken(); + * } + * * @param string $token 登入階段token + * @throw \UElearning\User\Exception\LoginTokenNoFoundException * @since 2.0.0 */ public function logout($token) { - // TODO: Fill code in + + $db = new Database\DBUserSession(); + + // 如果有找到此登入階段 + if( $db->queryByToken($token) ) { + $db->logout($token); + } + // 沒有此登入階段 + else { + throw new Exception\LoginTokenNoFoundException($token); + } } /** * 將其他已登入的裝置登出 * @param string $token 登入階段token + * @return int 已登出數量 + * @throw \UElearning\User\Exception\LoginTokenNoFoundException * @since 2.0.0 */ public function logoutOtherSession($token) { - // TODO: Fill code in + + // 先從token查詢到使用者是誰 + $user_id = $this->getUserId($token); + + $db = new Database\DBUserSession(); + // 如果有找到此登入階段 + if( $db->queryByToken($token) ) { + // 查詢者個使用者的所有登入階段 + $allSession = $db->queryLoginByUserId($user_id); + + // 紀錄已登出數量 + $logoutTotal = 0; + if(isset($allSession)) { + // 將所有非此Token的裝置登出 + foreach($allSession as $key=>$thisSession) { + if($thisSession['token'] != $token) { + $this->logout($thisSession['token']); + $logoutTotal++; + } + } + }; + return $logoutTotal; + } + // 沒有此登入階段 + else { + throw new Exception\LoginTokenNoFoundException($token); + } } - /** * 取得使用者物件 * @param string $token 登入階段token * @return User 使用者物件 + * @throw \UElearning\User\Exception\LoginTokenNoFoundException * @since 2.0.0 */ public function getUser($token) { - // TODO: Fill code in + $userId = $this->getUserId($token); + return new User($userId); + } + + /** + * 取得使用者ID + * @param string $token 登入階段token + * @return string 使用者ID + * @throw \UElearning\User\Exception\LoginTokenNoFoundException + * @since 2.0.0 + */ + public function getUserId($token) { + $db = new Database\DBUserSession(); + $sessionArray = $db->queryByToken($token); + if(isset($sessionArray)) return $sessionArray['user_id']; + else throw new Exception\LoginTokenNoFoundException($token); } /** @@ -65,7 +193,7 @@ class UserSession { * @since 2.0.0 */ public function getTokenInfo($token) { - // TODO: Fill code in + // TODO: 取得登入資訊 } // ======================================================================== @@ -77,7 +205,7 @@ class UserSession { * @since 2.0.0 */ public function getUserLoginInfo($userId) { - // TODO: Fill code in + // TODO: 取得所有此使用者已登入的登入階段資訊 } /** @@ -86,8 +214,20 @@ class UserSession { * @return int 所有以登入的數量 * @since 2.0.0 */ - public function getUserLoginTotal($userId) { - // TODO: Fill code in + public function getLoginTotalByUserId($userId) { + + // 確保若無此使用者則丟例外 + $user = new User($userId); + + // 查詢者個使用者的所有登入階段 + $db = new Database\DBUserSession(); + $allSession = $db->queryLoginByUserId($userId); + + // 回傳目前已登入的裝置數 + if(isset($allSession)) { + return count($allSession); + } + else return 0; } /** @@ -99,15 +239,26 @@ class UserSession { * @since 2.0.0 */ public function getUserAllInfo($userId) { - // TODO: Fill code in + // TODO: 取得所有此使用者全部的登入階段資訊 } /** * 將此使用者全部登入階段登出 * @param string $userId 使用者帳號名稱 + * @return int 已登出數量 + * @throw UElearning\User\Exception\UserNoFoundException * @since 2.0.0 */ - public function logoutUserAllSession($userId) { - // TODO: Fill code in + public function logoutByUser($userId) { + + // 確保若無此使用者則丟例外 + $user = new User($userId); + + // 登出此使用者所有登入階段 + $db = new Database\DBUserSession(); + + $logoutTotal = 0; + $logoutTotal = $db->logoutByUserId($userId); + return $logoutTotal; } } \ No newline at end of file