# 5-9 、 實作登入功能 ###### tags: `BE101` `PHP` `2020十一月第一周` `進度筆記` `Lidemy心得` --- ## 實作登入頁面 - 跟註冊功能頁面很相似。 在 login.php 底下: - 修改登入標題 `<h1 class="board__title">登入</h1>` 。 - 連接 handle_login.php 檔案: ``` <form class="board__new-comment-form" method="POST" action="handle_login.php"> ``` --- ### 輸入帳號密碼提交後會到哪? handle_login 檔案內: - header 導回 `login.php?errCode=1` 。 - 判斷使用者登入成功與否(sql) , 即是判斷資料庫內有無這筆帳號密碼: ``` $sql = sprintf( "select * from users where username='%s' and password='%s'", $username, $password //這樣可以去找資料庫有無這筆 username 和 password 。 ``` 因為不會有 duplicate entry 情形,所以刪掉了: ``` $code = $conn->errno; if ($code === 1062) { header('Location: index.php?errCode=2'); } ``` - 資料導出後要檢查有無到資料。 ``` $result = $conn->query($sql); if (!$result) { die($conn->error); } if ($result->num_rows) { // 如果有資料的話登入成功 echo '登入成功!'; } else { header("Location: login.php?errCode=2"); } ``` - 最後要導回 index.php 。 ``` header("Location: login.php?errCode=2"); //如果 errCode=2 表示帳號或是密碼輸入錯誤 ``` - 這邊在 handle_login.php 修正後也要回 login.php 修正。 ``` $msg = '帳號或是密碼輸入錯誤' ``` - 登入成功後應該要導回首頁,而如何在登入成功後要如何知道登入確實成功了? - 在 http 中,"首頁"跟"登入成功"是兩個不同的頁面。 - http 是沒有狀態,每發一個 request 都是新的,因此要讓這兩個頁面連接(記住登入狀態)。 --- # 5-10 、 該怎麼記住登入狀態? ###### tags: `BE101` `PHP` `2020十一月第一周` `進度筆記` `Lidemy心得` ## HTTP 無狀態登入機制(stateless) - 兩個 request 對同一個 index.php 請求時 , server 不知道兩者是否同一個請求。 - Cookie - 可以記錄 HTTP 狀態(可以用 dev tools 看 Cookies)。 - 可以從 Cookie 查 Domain 、 Expire 、 來源...等 。 *** 參考資料: [An overview of HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview) [隱藏欄位介紹](https://lucrelin.blogspot.com/2016/11/stateless-protocol-vs-stateful-protocol.html) https://dotblogs.com.tw/jimmyyu/2010/10/16/difference-between-stateful-and-stateless [HTTP Cookies 和Session 使用. HTTP 是一個「無狀態 ...](https://medium.com/%E9%BA%A5%E5%85%8B%E7%9A%84%E5%8D%8A%E8%B7%AF%E5%87%BA%E5%AE%B6%E7%AD%86%E8%A8%98/%E7%AD%86%E8%A8%98-http-cookie-%E5%92%8C-session-%E4%BD%BF%E7%94%A8-19bc740e49b5) *** ## 紀錄 HTTP 狀態 ==設置 Cookie:== ``` 瀏覽器(收到 header 後)會幫忙設置 user_id=1 並存在瀏覽器內後 request 登入 ↔ Server (經由 server 設定的 Set-Response header Cookie: `user_id=1`) ↔ index.php ``` ==因此之後訪問同樣頁面,瀏覽器會自動帶上 Cookie :== - 瀏覽器會自動把符合條件的 Cookie 帶入。 - 條件: 沒有過期 , Domain 跟路徑吻合。 - Domain 只能設置自己底下的 cookie 。 - 不同家不同源的不能彼此干擾(安全性問題,會盜走他人 Cookie)。 - 所以伺服器看到 cookie 後就能知道瀏覽器有設置的 `user_id=1` 並得知是誰。 ``` <?php $value = 'something from somewhere'; setcookie("TestCookie", $value); setcookie("TestCookie", $value, time()+3600); /* expire in 1 hour */ setcookie("TestCookie", $value, time()+3600, "/~rasmus/", "example.com", 1); ?> ``` - 一樣有 key & value 。 ## Cookie 簡介與實作 開啟 handle_login.php 檔案: - `setcookie()` ``` if ($result->num_rows) { // 如果有資料的話登入成功 $expire = time() + 3600 * 24 * 30; // 30 day setcookie("username", $username, $expire); // "字串", $傳進來的值, $過期時間 header("Location: index.php"); } else { header("Location: login.php?errCode=2"); } ``` - 用 dev tool 看 Network 的 handle_login.php 檔案: ``` Response header 就會是 Set-Cookie 傳回來。 ``` - 用 dev tool 看 Application → Cookies 底下就會多了一個 Name 和 Value 。 - 在 handle_login.php 檔案下回傳 Set-Cookie 給瀏覽器。 - 只要在這個 `/be101/board` 資料夾底下的檔案就能共享這個 cookie 。 - 但如果在 `/be101` 底下開了其他資料夾就無法共享 cookie 。 - 如果發現 cookie 失效,有時候要更改其 Pathway 。 - 如果 Path 底下設定成 `/` 就可以是 localhost 底下所有頁面都能共享這個 cookie 。 ## 設置 index.php ``` <?php require_once("conn.php"); $result = $conn->query("select * from comments order by id desc"); if (!$result) { die('Error:' . $conn->error); } $username = NULL; // 帳號不是 no 的話,即登入狀態 if (!empty($_COOKIE['username'])) { $username = $_COOKIE['username']; // username 即帳號 } ?> ``` - 記住登入狀態。 - 檢查 username 是不是空的: ``` <main class="board"> <div> <?php if (!$username) { ?> // 如果 username 是空的話顯示註冊和登入的值 <a class="board__btn" href="register.php">註冊</a> <a class="board__btn" href="login.php">登入</a> <?php } else { ?> <a class="board__btn" href="logout.php">登出</a> <?php } ?> </div> ``` - 如果 username 不是空的話就不會顯示註冊和登入按鈕(表示為登入狀態)。 - 已經有登入的話,顯示登出。 ## 設置登出功能 - 可以把 cookie 的 `user_id` 清空。 - 把過期時間(expire)設成現在時間 - 3600 , 這樣就變成"過去的時間"規格。 ``` time()-3600 // 即過期了 ``` logout.php 檔案: ``` <?php setcookie("username", "", time() - 3600); header("Location: index.php"); ?> ``` 過期的 cookie 被拋棄掉後,瀏覽器代的下個 request 就不會有這個 cookie 。 ## 如果沒有登入的話看不到底下的留言區塊 index.php 底下: ``` <form class="board__new-comment-form" method="POST" action="handle_add_comment.php"> <textarea name="content" rows="5"></textarea> <?php if ($username) { ?> // 如果 username 登入的話就看得到留言表單 <input class="board__submit-btn" type="submit" /> <?php } else { ?> <h3>請登入發布留言</h3> <?php } ?> </form> ``` - 沒有登入的話,會看到"請登入發布留言"文字。 - 因為 nickname 變成從 cookie 中去抓取使用者資訊出來。 - 所以也要更動 handle_add_comment.php 留言板資訊: ``` <?php require_once('conn.php'); if ( empty($_POST['content']) ) { header('Location: index.php?errCode=1'); die('資料不齊全'); } $username = $_COOKIE['username']; $user_sql = sprintf( "select nickname from users where username='%s'", $username ); // 這樣就能從資料庫中去抓出 nickname , 不把 nickname 存在 cookie 內是因為日後可能更改 $user_result = $conn->query($user_sql); $row = $user_result->fetch_assoc(); $nickname = $row['nickname']; // 還沒有做錯誤處理 $content = $_POST['content']; // 用 POST 會出現確認重新提交表單 $sql = sprintf( "insert into comments(nickname, content) values('%s', '%s')", $nickname, $content ); $result = $conn->query($sql); if (!$result) { die($conn->error); } header("Location: index.php"); ?> ``` - Cookie 可以維持 HTTP 狀態的特性,從 Server 這用 `Set-Cookie()` Header 設置到瀏覽器內存起來。 - Response Header 給瀏覽器後,就會讓瀏覽器設定所要的 Cookie 。 - 在 handle_login.php 檔案下回傳 Set-Cookie 給瀏覽器。 - 瀏覽器接收到這個 Set-Cookie 後就會把想要設定的 Cookie 設定到瀏覽器內。 - 如 Cookie 內設置(紀錄) username 、 expire ...等。 - 之後發 request 到 index.php 時就會把設定的 Cookie 一起帶入。