ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [MongoDB] 3. 세션관리자 제작
    CSE/MongoDB 2015. 6. 13. 11:41
    3장 세션관리자 제작 시작하겠습니다!

     이번 장에서 구현할 세션관리자는

      웹사이트를 방문한 방문객의 HTTP 세션을 관리하고 세션 데이터를 저장하기 위해 몽고DB를 사용하는 모듈이다. 세션관리자는 사용자 autheticate, 활동 추적, 활동 인증, 로그 아웃 관리와 같은 기본적이지만 중요한 기능을 담당한다. 여기서는 객체지향형 프로그래밍 원칙을 사용해 모듈을 구현할 것이다.






     1. SessionManager 클래스 구현
     - 세션 저장/인출/처리를 위해 몽고DB에 만들어진 컬렉션을 사용하는 모듈인 SessionManager 클래스를 구현하겠다. 세션을 처리하기 위한 콜백 함수로 이 클래스의 인스턴스 메소드를 session_set_save_handler()로 등록할 것이다. 설계 목표는 다움과 같다.

      * 클래스가 구현한 세션은 Life Time이 1시간
      * 세션은 Timeout이 10분(이 시간동안 비활성이면 세션만료(ex: 자동 로그아웃 같은)) 
      * 세션 데이터는 세션을 표현하는 컬렉션의 다큐먼트 필드로 직렬화


     먼저, dbconnection.php라는 파일을 생성하여 아래 소스 넣는다.

    dbconnection.php 

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <?php
    class DBConnection {
    const HOST = 'localhost';
    const PORT = 27017;
    const DBNAME = 'myblogsite';
     
    private static $instance;
    public $connection;
    public $database;
     
    private function __construct() {
    $connectionString = sprintf('mongodb://%s:%d', DBConnection::HOST, DBConnection::PORT);
     
    try {
    $this->connection = new Mongo($connectionString);
    $this->database = $this->connection->selectDB(DBConnection::DBNAME);
    } catch (MongoConnectionException $e) {
    throw $e;
    }
    }
     
    static public function instantiate() {
    if (!isset(self::$instance)) {
    $class = __CLASS__;
    self::$instance = new $class;
    }
    return self::$instance;
    }
     
    public function getCollection($name) {
    return $this->database->selectCollection($name);
    }
    }
    ?>
    cs


     

    위의 DBConnection 클래스를 생성하여, 코드 재사용성을 증대시켰다. instantiate() 메소드를 호출하여 객체를 반환받아서, 
    getClollection() 메소드를 호출하여 컬렉션을 선택한다. 
     * 위 DBConnection은 주요 Design Pattern중 Singleton Pattern을 적용하였다. 



    다음으로, session.php를 생성, 아래 코드 넣는다.

    session.php

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    <?php
    require_once('dbconnection.php');
     
    class SessionManager {
    // Session을 저장할 Collection 이름
    const COLLECTION = 'sessions';
    // 10분 동안 비활성인 경우 Session 만료
    const SESSION_TIMEOUT = 600;
    // 1시간 후 Session 만료
    const SESSION_LIFESPAN = 3600;
    // Session Cookie Name
    const SESSION_NAME = 'mongosessid';
    const SESSION_COOKIE_PATH = '/';
    /*
    Web Application의 Domain Name 지정
    ex) mywebapp.com
    Local 환경에서 돌리지 않은 경우에는 빈 문자열 사용금지
    */
    const SESSION_COOKIE_DOMAIN = '';
     
    private $_mongo;
    private $_collection;
    // 현재 Session 표현
    private $_currentSession;
     
    public function __construct() {
    $this->_mongo = DBConnection::instantiate();
    $this->_collection = $this->_mongo->getCollection(SessionManager::COLLECTION);
    session_set_save_handler(
    array(&$this'open'),
    array(&$this'close'),
    array(&$this'read'),
    array(&$this'write'),
    array(&$this'destroy'),
    array(&$this'gc')
    );
    // Session Garbage Collection 기간 설정
    ini_set('session.gc_maxlifetime', SessionManager::SESSION_LIFESPAN);
    // Session Cookie 구성 설정 
    session_set_cookie_params(
    SessionManager::SESSION_LIFESPAN,
    SessionManager::SESSION_COOKIE_PATH,
    SessionManager::SESSION_COOKIE_DOMAIN
    );
    // Session Name으로 'mongosessid'로 'PHPSESSID'를 대체
    session_name(SessionManager::SESSION_NAME);
    session_cache_limiter('nocache');
    // Session Start
    session_start();
    }
     
    public function open($path$name) {
    return true;
    }
     
    public function close() {
    return true;
    }
     
    public function read($sessionId) {
    $query = array
    'session_id' => $sessionId,
    'timedout_at' => array('$gte' => time()),
    'expired_at' => array('$gte' => time() - SessionManager::SESSION_LIFESPAN)
    );
    $result = $this->_collection->findOne($query);
    $this->_currentSession = $result;
     
    if (!isset($result['data'])) {
    return '';
    }
    return $result['data'];
    }
     
    public function write($sessionId$data) {
    $expired_at = time() + self::SESSION_TIMEOUT;
    $new_obj = array(
    'data' => $data,
    'timedout_at' => time() + self::SESSION_TIMEOUT,
    'expired_at' => (empty($this->_currentSession)) ? time() + SessionManager::SESSION_LIFESPAN : $this->_currentSession['expired_at']
    );
     
    $query = array('session_id' => $sessionId);
    $this->_collection->update(
    $query,
    array('$set' => $new_obj),
    array('upsert' => True)
    );
    return True;
    }
     
    public function destroy($sessionId) {
    $this->_collection->remove(array('session_id' => $sessionId));
    return True;
    }
     
    public function gc() {
    $query = array('expired_at' =>array('$lt' => time()));
    $this->_collection->remove($query);
    return True;
    }
     
    public function __destruct() {
    session_write_close();
    }
    }
     
    $session = new SessionManager();
    ?>
    cs






     위 코드를 작성하여 Session을 접근한다면, php문법의 require/include를 통하여 접근한다.




    2. User 클래스 구현
     테스트용으로 사용자 계정을 여러개 생성한다.
     먼저 몽고DB 내에 사용자 계정을 생성하기 위한 create_users.php를 작성한다.



    create_users.php

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    <?php
    require('dbconnection.php');
    $mongo = DBConnection::instantiate();
    $collection = $mongo->getCollection('users');
     
    $users = array(
    array(
    'name' => 'Luke Skywalker',
    'username' => 'jedimaster23',
    'password' => md5('usetheforce'),
    'birthday' => new MongoDate(strtotime('1971-09-29 00:00:00')),
    'address' => array(
    'town' => 'Mos Eisley',
    'planet' => 'Tatooine'
    )
    ),
     
    array(
    'name' => 'Leia Organa',
    'username' => 'princessleia',
    'password' => md5('eviltween'),
    'birthday' => new MongoDate(strtotime('1976-10-21 00:00:00')),
    'address' => array(
    'town' => 'Aldera',
    'planet' => 'Alderaan'
    )
    ),
     
    array(
    'name' => 'Chewbacca',
    'username' => 'cewiethegreat',
    'password' => md5('loudgrowl'),
    'birthday' => new MongoDate(strtotime('1974-05-19 00:00:00')),
    'address' => array(
    'town' => 'Kachiro',
    'planet' => 'Kashyyk'
    )
    )
    );
     
    foreach($users as $user) {
    try {
    $collection->insert($user);
    } catch(MongoCursorException $e) {
    die($e->getMessage());
    }
    }
     
    echo 'Users created successfully';
    ?>
    cs




    생성 후, localhost/create_users.php를 수행하여 사용자 계정 데이터를 생성한다.
    다음, user.php를 작성한다.




    user.php

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    <?php
    require_once('dbconnection.php');
    require_once('session.php');
     
    class User {
    const COLLECTION = 'users';
    // collection은 users 사용
     
    private $_mongo;
    private $_collection;
    private $_user;
     
    // User Class Construct
    public function __construct() {
    // DBConnection으로부터 DB를 받아와 Collection 선택
    $this->_mongo = DBConnection::instantiate();
    $this->_collection = $this->_mongo->getCollection(User::COLLECTION);
     
    // Login 상태면 user data 불러옴
    if ($this->isLoggedIn()) $this->_loadData();
    }
     
    // Login 상태인지 확인
    public function isLoggedIn() {
    return isset($_SESSION['user_id']);
    }
     
    // username과 password를 db에서 query하여 authenticate
    public function authenticate($username$password) {
    $query = array
    'username' => $username,
    'password' => md5($password)
    );
     
    $this->_user = $this->_collection->findOne($query);
    if (empty($this->_user)) return False;
    $_SESSION['user_id'] = (string) $this->_user['_id'];
    return True;
    }
     
    public function logout() {
    unset($_SESSION['user_id']);
    }
     
    public function __get($attr) {
    if (empty($this->_user)) 
    return Null;
     
    switch ($attr) {
    case 'address': {
    $address = $this->_user['address'];
    return sprintf("Town: %s, Planet: %s"$address['town'], $address['planet']);
    }
    case 'town': {
    return $this->_user['address']['town'];
    }
    case 'planet': {
    return $this->_user['address']['planet'];
    }
    case 'password': {
    return NULL;
    }
    default
    return (isset($this->_user[$attr])) ? $this->_user[$attr] : NULL;
    }
    }
     
    private function _loadData() {
    $id = new MongoId($_SESSION['user_id']);
    $this->_user = $this->_collection->findOne(array('_id' => $id));
    }
    }
    ?>
    cs



    자 마지막 단계만을 남겨둔 상태이다.




    3. 로그인, 로그아웃, 프로파일 페이지 구현
     로그인 폼을 생성하고, 인증을 통하여 프로파일 페이지로 넘어가게해서 사용자의 기본 정보를 보이게 한다. 프로파일 페이지 내에 로그아웃 기능을 추가하면 끝이다.




    먼저 login.php를 작성하자.

    login.php 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    <?php
    $action = (!empty($_POST['login']) && ($_POST['login'] === 'Log In')) ? 'login' : 'show_form';
     
    switch ($action) {
    case 'login': {
    require('session.php');
    require('user.php');
     
    $user = new User();
    $username = $_POST['username'];
    $password = $_POST['password'];
     
    if ($user->authenticate($username$password)) {
    header('location: profile.php');
    exit;
    else {
    $errorMessage = "Username/password did not match";
    break;
    }
    }
     
    case 'show_form':
    default:
    $errorMessage = NULL;
    }
    ?>
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <link rel="stylesheet" href="style.css"/>
            <title> User Login </title>
        </head>
        <body>
            <div id="contentarea">
                <div id="innercontentarea">
                    <h1> Log in here </h1>
                    <div id="login_box">
                        <div class="inner">
                            <form id="login" action="login.php" method="POST" accept-charset="utf-8">
                                <ul>
                                    <?php if (isset($errorMessage)):?>
                                        <li><?php echo $errorMessage;?></li>
                                    <?php endif;?>
                                    <li>
                                        <label> UserName </label>
                                        <input class="textbox" tabindex="1" type="text" name="username" autocomplete="off"/>
                                    </li>
                                    <li>
                                        <label> Password </label>
                                        <input class="textbox" tabindex="2" type="password" name="password"/>
                                    </li>
                                    <li>
                                        <input id="login_submit" name="login" tabindex="3" type="submit" value="Log In" />
                                    </li>
                                    <li class="clear"></li>
                                </ul>
                            </form<!-- End of form login -->
                        </div<!-- End of div inner -->
                    </div<!-- End of div login_box -->                
                </div<!--End of div innercontentarea -->
            </div>
        </body>
    </html>
    cs

     





    다음, profile.php

    profile.php

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <?php
    require('session.php');
    require('user.php');
    $user = new User();
     
    if (!$user->isLoggedIn()) {
    header('location: login.php');
    exit;
    }
    ?>
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <link rel="stylesheet" href="style.css"/>
            <title> Welcome <?php echo $user->username;?> </title>
        </head>
        <body>
            <div id="contentarea">
                <div id="innercontentarea">
                <a style="float:right;" href="logout.php"> Log out </a>
                    <h1> Hello <?php echo $user->username;?> </h1>
                    <ul class="profile_list">
                        <li>
                            <span class="field">Username</span>
                            <span class="value">
                                <?php echo $user->username;?>
                            </span>
                            <div class="clear"></div>
                        </li>
                        <li>
                            <span class="field">Name</span>
                            <span class="value">
                                <?php echo $user->name;?>
                            </span>
                            <div class="clear"></div>
                        </li>
                        <li>
                            <span class="field">Birthday</span>
                            <span class="value">
                                <?php echo date('j F, Y'$user->birthday->sec);?>
                            </span>
                            <div class="clear"></div>
                        </li>
                        <li>
                            <span class="field">Address</span>
                            <span class="value">
                                <?php echo $user->address;?>
                            </span>
                            <div class="clear"></div>
                        </li>
                    </ul>
                </div<!--End of div innercontentarea -->
            </div>
        </body>
    </html>
    cs







    마지막으로, logout.php

    logout.php
     

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
    require_once('session.php');
    require_once('user.php');
    $user = new User();
    $user->logout();
    header('location: login.php');
    exit;
     
    ?>
    cs



     

    CSS는 첨부되어있는 파일로 적용시킨다.



    이제! 웹에서 작성한 웹앱을 확인해보자! localhost/login.php로 접근해보자!
    아래와 같이 나오는데, UserName과 password를 아까 생성한 계정중 한개로 입력하자! 그리고 Log In 클릭!!


     









    그럼 다음처럼 나온다면 이번장은 클리어 한 것이다!



     




    이것으로 세션관리자 제작에 관한 포스팅을 마치겠습니다








    댓글

Designed by Tistory.