本篇文章作為網站實戰開發的第八篇,我們繼續實作登入狀態。
--------系列簡介--------
網站系統可說是現在最多學子與新人想要入門的一個領域,
這個原本屬於新興的領域,這幾年來也累積許多年的知識與 pattern ,
在有限的環境(stateless)與有限的伺服器端、瀏覽器資源下,
成為許多人進入程式的一塊入門鐵板(?)。
這個系列希望能夠就網站系統設計幾個門檻著手,
這是設定給初心者作為學習,給同好們作為回顧,
重新認識有關網站系統的每個環節。
OK
在昨天我們討論到切分 view 跟畫面的細節,今天我們要接著實作接下來的部份。
首先是登入,一樣照慣例是建好 Login View
application/views/login.php
<?php include("_site_header.php"); ?>
<div class="container">
<?php include("_content_nav.php") ?>
<div class="content">
<form action="<?=site_url("user/logining")?>" method="post" >
<?php if(isset($errorMessage)){ ?>
<div class="alert alert-error"><?=$errorMessage?></div>
<?php } ?>
<table>
<tr>
<td>帳號</td>
<?php if(isset($account)){ ?>
<td><input type="text" name="account"
value="<?=htmlspecialchars($account)?>" /></td>
<?php }else{ ?>
<td><input type="text" name="account" /></td>
<?php } ?>
</tr>
<tr>
<td> 密碼 </td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td colspan="2">
<a href="<?=site_url("/")?>">取消</a>
<input type="submit" class="btn" value="送出" />
</td>
</tr>
</table>
</form>
</div>
</div>
接著實作 controller 的細節
application/controllers/user.php
public function login(){
session_start();
if(isset($_SESSION["user"]) && $_SESSION["user"] != null){ //已經登入的話直接回首頁
redirect(site_url("/")); //轉回首頁
return true;
}
$this->load->view(
"login",
Array( "pageTitle" => "發文系統 - 會員登入" )
);
}
public function logining(){
session_start();
if(isset($_SESSION["user"]) && $_SESSION["user"] != null){ //已經登入的話直接回首頁
redirect(site_url("/")); //轉回首頁
return true;
}
$account = trim($this->input->post("account"));
$password = trim($this->input->post("password"));
$this->load->model("UserModel");
$user = $this->UserModel->getUser($account,$password);
if($user == null){
$this->load->view(
"login",
Array( "pageTitle" => "發文系統 - 會員登入" ,
"account" => $account,
"errorMessage" => "使用者或密碼錯誤"
)
);
return true;
}
$_SESSION["user"] = $user;
redirect(site_url("/")); //轉回首頁
}
public function logout(){
session_start();
session_destroy();
redirect(site_url("/user/login")); //轉回登入頁
}
詳細完整原始碼請見:https://gist.github.com/3893200
大家可以看得出來都是一些邏輯與狀態的紀錄,
相信在我們之前介紹的內容後,讀者應該可以餘刃有餘的閱讀這些程式碼。
接著我們要繼續實作還沒實作完的 model ,從資料庫查詢這個帳號密碼是否存在。
(注意,這裡我們是用明碼實作密碼,正式環境中請記得透過 SHA1或 MD5 加密。)
application/models/usermodel.php
public function getUser($account,$password){
$this->db->select("*");
$query = $this->db->get_where("user",Array("account" => $account, "password" => $password ));
if ($query->num_rows() > 0){ //如果數量大於0
return $query->row(); //回傳第一筆
}else{
return null;
}
}
詳細原始碼請見https://gist.github.com/3893198
最後則是修改一下,全站狀態登入狀態的判斷:
application/views/_content_nav.php
<!-- Content Header -->
<div class="content-header">
<div class="navbar navbar-inverse">
<div class="navbar-inner">
<a class="brand" href="<?=site_url("/")?>">The Articles</a>
<ul class="nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">My Articles</a></li>
</ul>
<!-- login status -->
<?php if(isset($_SESSION["user"]) && $_SESSION["user"] != null){ ?>
<ul class="nav pull-right">
<li><a href="#">Hi <?=$_SESSION["user"]->Account?></a></li>
<li class="divider-vertical"></li>
<li><a href="<?=site_url("user/logout")?>">登出</a></li>
</ul>
<?php }else{ ?>
<ul class="nav pull-right">
<li><a href="<?=site_url("user/login")?>">登入</a></li>
<li class="divider-vertical"></li>
<li><a href="<?=site_url("user/register")?>">註冊</a></li>
</ul>
<?php } ?>
</div>
</div>
</div>
因為我們使用了 session 作為判斷,所以每一個需要用到他的 controller ,
都需要 call session_start() ,這樣實在是有一點沒效率,對吧? ;)
在 CI 裡面有很簡單的方式,可以透過 controller 一致性的進行共同性的動作,
如 session_start 之類的操作。
今天我們很簡單的實作了基本的登入狀態實作,接下來明天我們將繼續實作發文細節,
如果有餘裕的話,我們會再來加以說明怎麼做加密的密碼儲存與驗證。:)
那麼,明天見囉。
補充一下如何避免逐一 function 撰寫 session_start() ,
在 CI 裡面有很簡單的方式,可以讓 controller 一致性的,
進行如 session_start 之類的操作。
這裡的作法相當單純,我們只要讓 CI Controller 繼承一個我們自己定義的父類別,
並且在 CI controlelr 設定為繼承,並且在該類別建構時預先做好我們希望他做的事情即可。
這個類別比較特別,是要放在 application/core 裡面,作法如下:
1.建立 MY_Controller 檔案
application/core/MY_Controller.php
內容為
<pre class="c" name="code">
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php
class MY_Controller extends CI_Controller {
public function __construct(){
parent::__construct();
$this->_init();
}
protected function _init(){
session_start();
}
}
2.設定各Controller 從 extend CI_Controller 成為 MY_Controller。
如
class User extends CI_Controller
改為
class User extends MY_Controller
然後移除相關已存在 controller method 的 session_start() ,這樣就可以囉,
而且這個父類別也可以用來定義一些有用的共用元素,像是取得 user IP 之類的共同操作。
另外可能有人會提到 CI 內建有他自己的 session 實作,
不過內建的 session 實作比較複雜,而且需要設定 secret key 。
筆者自己是也不常用他,加上我們之前已經介紹過 php 的 SESSION 機制,
所以就直接用 php 內建的 SESSION 機制進行介紹囉。;)
有關 CI Session 的 reference :
http://codeigniter.com/user_guide/libraries/sessions.html
以下內容是俺跟某前輩對話整理~~!
CI的session理論上是session 但是ci是用cookie
$this->session->userdata();
這東東雖然是掛名session實際上是cookie
至於說ci能不能用「正港」的session
兩種方式
一種就是你用傳統的session寫法就好了
另一個是寫外掛
http://www.codeigniter.org.tw/user_guide/libraries/sessions.html
你仔細看他第一行的說明
CI 允許你切換為 db session 實作啊 :P
ex. sess_use_database
基本上 session 這個主題本身就很值得一談了,特別是扯到 clustering 分散式結構時,
memory session 通常會有問題而會需要採取 db session 或 cookie session 的狀況。
不過一般而言 cookie session 因為 cookie 長度有上限,所以使用上有所侷限,
另一方面是安全性也有顧慮,所以不會拿來放太重要的東西;
就算要放,也會放一定時間內就會過期的資訊。
tony1223提到:
session 這個主題本身就很值得一談了,特別是扯到 clustering 分散式結構時,
memory session 通常會有問題
俺前輩是寫外掛去使用
俺因為是存不重要的東西所以用CI的就好了~~!
全站狀態登入狀態的判斷那邊
不需要session_start()嗎?
如果是在某些controller沒預先載入session_start()的頁面,
是不是isset($_SESSION["user"]) 就會一直是true了?
welcome那一頁不是user這個Controller
所以唯獨這一頁 要在welcome_message.php加上 <? session_start() ?>
前一篇文章底下
作者有延伸補充MY_controller加上session_start的用法
但是你會發現即使這麼加
有登入帳號 首頁上方的nav_bar呈現的畫面還是不對
這個問題是welcome_message並沒有session_start
因為它並不屬於任何controller底下控制的頁面(?
詳細參考樓上@piece601的留言
我也是找好久 才看到他的留言
老師您好,近期因為課業需要在瀏覽這篇系列文,但是到這個登入篇就失敗了
我有照您補充說明 增加MY_Controller這個檔案也有刪除user.php裡面的session_start()和@piece601老師補充的要在welcome_message.php增加session_start()
但是全站登入判斷的狀態條依然是登入和註冊,註冊成功和登入後並沒有轉換成登出和發文
如圖上,這是登入後跳回主頁的畫面
想請教老師 具體應該刪除user.php哪個方法的session_start()跟在welcome_message.php哪裡增加session_start()
謝謝老師!