5-1:管理画面の作成(ログイン、ダッシュボード)

2019/05/31

概要

現状のコーポレートサイトでは、Newsを投稿したい場合、page.htmlのように新しいhtmlファイルを作成して、トップページにhtmlファイルのリンクを表示させるしか方法がありません。
実際にこの運用だと、手間がかかってしまいます。
今回は、コーポレートサイト内でブログのように、記事を投稿して内容を動的に表示できるように簡易CMS機能を実装していきます。
このパートでは、CMS機能を実装するのに必要な管理画面を作成していきます。
管理画面は、ユーザーではなくサイトの管理者だけが閲覧できる画面です。
ユーザーは閲覧できないようにログイン認証機能を設置していきます。

フォルダ階層

管理画面は、ユーザーが見るページとは異なる階層にまとめてフォルダを置きたいと思います。
adminというフォルダを作成して、その中で作業を行っていきます。

corporate-site

corporate-site/adminフォルダ内

完成イメージ

管理画面ログイン

ダッシュボード

事前準備

今回、管理画面はcorporate-site/admin/~~~というパスでアクセスするようにします。
なので、管理画面系のファイルはadminというフォルダを作ってそのなかにファイルを作成していくようにします。
そのために、corporate-siteの中にadminというフォルダを作成しておきます。

全体の手順

今回の手順は以下の通りです。
すべてローカル環境で作業します。

  1. 管理画面ログインページの作成
  2. ログイン認証機能の実装

管理画面ログインページの作成

今回は簡易的なログイン機能を作成します(データベースを参照しません)。
IDが[admin@admin.com]、Passwordが[password01]だったら認証成功!
それ以外は認証失敗。
という判断をして、ログイン状態をPHPのSessionで保持する仕組みを作ります。
ログイン状態をSessionに保存することで、ログインしていないと閲覧できないページにアクセスしたときに、ログイン状態を確認して表示非表示を実装することができます。

Sessionとは

PHPのSessionとはコンピュータに一時的にデータを保存する仕組みのことです。
ログインの状態やショッピングカートの中身など一時的に情報を保持したいときに利用します。

CookieとSession

Sessionと同じような機能としてCookieがあります。
Cookieとは、クライアント(ブラウザ)にデータを保存する仕組みです。HTTPヘッダーに格納されるデータでヘッダーをみてみると中身が丸見えです。
Sessionは、sessionを開始するとセッションIDを発行し、セッションIDに紐付いたデータファイルをサーバ側で保存します。そして、クライアント側ではセッションIDをCookieに保存する仕組みです。HTTPヘッダーにはセッションIDが格納されいるだけなので、データの詳細は見えません。
今回はSessionを使って実装していきます。

ログインページの作成

事前準備で作成したadminフォルダの中に、index.htmlとstyles.cssを作成します。
管理画面は通常PCでのみ閲覧するので、レスポンシブ対応はしません。
admin/index.html

<!DOCTYPE html>
<html>

<head>
    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-13xxxxxxxxx"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag() { dataLayer.push(arguments); }
        gtag('js', new Date());

        gtag('config', 'UA-13xxxxxxxxx');
    </script>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>管理画面ログイン</title>

    <link rel="icon" href="favicon.ico">

    <!-- css -->
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <div class="login-wrapper" id="login">
        <div class="container">
            <div class="login">
                <div class="login-wrapper-title">
                    <h3>ログイン</h3>
                </div>
                <form class="login-form">
                    <div class="form-group">
                        <p>メールアドレス</p>
                        <input type="email" name="email" required>
                    </div>
                    <div class="form-group">
                        <p>パスワード</p>
                        <input type="password" name="password" required>
                    </div>
                    <button type="submit" class="btn btn-submit">ログイン</button>
                </form>
            </div>
        </div>
    </div>
</body>

</html>

admin/styles.css

/* css reset */
@charset "utf-8";
html, body, div, span, iframe,
h1, h2, h3, h4, h5, h6, p, img, a,
ol, ul, li,
form, label,
table, tbody, tfoot, thead, tr, th, td,
article,footer, header, menu, nav, section {
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  list-style: none;
  text-decoration: none;
  vertical-align: baseline;
}

/* 共通CSS */
.container {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 15px;
}

html {
    min-height: 100%;
    position: relative;
}

.wrapper {
    padding: 80px 0;
    margin-bottom: 80px;
}

    .wrapper-title {
        margin-bottom: 30px;
        border-bottom: 1px solid #a2aab0;
    }

    .wrapper-title h3{
        font-size: 25px;
        color: #3e3e3b;
    }

    .wrapper-title p {
        font-size: 13px;
        color: #a2aab0;
    }

.btn {
    padding: 10px 30px;
    font-size: 15px;
    border-radius: 20px;
    border: none;
    margin-top: 20px;
}

.btn:hover{
    cursor: pointer;
    opacity: .8;
}

.btn-submit {
    background-color: #4c586f;
    color: #fff;
}

/* login */
.login-wrapper {
    text-align: center;
    padding: 80px 0 ;
}
    .login-wrapper-title {
        margin-bottom: 30px;
    }

    .login-wrapper-title h3{
        font-size: 25px;
        color: #3e3e3b;
    }

    .login-wrapper-title p {
        font-size: 13px;
        color: #a2aab0;
    }

.login {
    padding: 50px 0; 
    width: 50%;
    margin: 0 auto;
    background-color: #ebeced;
}
    .login-form {
        width: 70%;
        margin: 0 auto;
    }

    .login-form input {
        border: none;
        width: 100%;
        height: 25px;
    }

    .login-form p {
        text-align: left;
    }

    .form-group {
        margin-bottom: 10px;
    }

corporate-site/adminにアクセスして確認します。
シンプルなログイン画面ができました。

ダッシュボード作成

次に、ログイン認証が成功した場合に表示されるダッシュボード画面を作成します。
ダッシュボード画面では、管理画面で行える機能の一覧を表示させていきます。
今回は記事の投稿や編集など記事の管理を行いますので、「記事管理」メニューを1つ表示させておきます。
adminフォルダにdashboard.htmlを作成します。

fontawesome

メニューアイコンはfontawesomeを使います。
fontawesomeとは、Web上でよく利用されるアイコンをアイコンフォントという文字として使うことができるようにしたツールです。
アイコンの種類がとても豊富で無料で利用できるアイコンもたくさんあります。
公式サイトからリンクをコピーしてstylesheetのようにタグを追加しておけば利用できます。

<a href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"></a>

fontawesomeの公式サイトから欲しいアイコンを検索して利用します。
記事管理用のアイコンを探したい場合、公式サイトのiconsをクリックし、検索窓で「newspaper」などと検索します。
該当するアイコンが表示されます。

有料アイコンと無料アイコンがあるのですが、今回は無料アイコンの中から左から2番目のアイコンを選択しました。
クリックすると、ページが移動します。

赤線で引いたタグをコピーしてhtmlに埋め込むとアイコンが表示されるので、コピーします。
コピーできたら、htmlに入れてみます。
admin/dashboard.html

<!DOCTYPE html>
<html>

<head>
    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-13xxxxxxxxx"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag() { dataLayer.push(arguments); }
        gtag('js', new Date());

        gtag('config', 'UA-13xxxxxxxxx');
    </script>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>ダッシュボード</title>

    <link rel="icon" href="favicon.ico">
    <!-- fontawesome -->
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
        integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">

    <!-- css -->
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <header>
        <div class="container">
            <div class="header-logo">
                <h1><a href="dashboard.html">管理画面</a></h1>
            </div>

            <nav class="menu-right menu">
                <a href="index.html">ログアウト</a>
            </nav>
        </div>
    </header>
    <main>
        <div class="wrapper">
            <div class="container">
                <div class="wrapper-title">
                    <h3>ダッシュボード</h3>
                </div>
                <div class="boxs">
                    <a href="news.php" class="box">
                        <i class="far fa-newspaper icon"></i><!-- fontawesome利用部分 -->
                        <p>記事管理</p>
                    </a>

                </div>
            </div>
        </div>
    </main>
    <footer>
        <div class="container">
            <p>Copyright @ 2018 SQUARE, inc</p>
        </div>
    </footer>
</body>

</html>

styles.cssも追加します。
admin/styles.css

/* dashboard.html */
/* header */
header {
    width: 100%;
    height: 60px;
    background-color: #ebeced;
    position: fixed;
    z-index: 1;
}

.header-logo h1{
    float:left;
    padding-right: 15px;
    font-size: 18px;
}

.header-logo a {
    color: #4c586f;
    line-height: 60px;
}

    .menu-icon {
        display: none;
    }

    .menu-right {
        float:right;
        line-height: 60px;
    }

    .menu-right a {
        color: #4c586f;
    }

    .menu-right a:hover {
        color: #a2aab0;
    }

/* boxs */
.boxs {
        display: flex;
    }

    .box {
        display: inline-block;
        width: 20%;
        height: 200px;
        border:1px solid #ebeced;
        margin: 0 5px;
        text-align: center;
        color: #3e3e3b;
        box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.29);
        }
    .box:hover {
        color: #4c586f;
    }
    .icon{
        font-size: 80px;
        height: 130px;
        line-height: 130px;
    }
    .box p {
        font-size: 20px;
        font-weight: bold;
    }

/* footer */
footer {
    height: 80px;
    background-color: #4c586f;
    text-align: right;
    bottom: 0;
    position: absolute;
    width: 100%;

}

    footer p {
        line-height: 80px;
        color: #fff;
        font-size: 12px;
    }

corporate-site/admin/dashboard.htmlにアクセスして確認します。
ダッシュボードができました。

認証機能の作成

認証機能を実装していきます。
認証機能の流れは図の通りです。
index.htmlからEmailとPasswordをcheck.phpに送り、check.phpファイルでログインチェックを行います。
ログイン許可する場合は、sessionにログインステータスをtrueで保存し、dashboard.htmlにリダイレクトさせます。
ログイン不可の場合、index.htmlにリダイレクトさせます。

index.htmlのform設定

formにactionとmethodを追加します。
(まだcheck.phpは作成していませんが)check.phpにpostで値を送りたいので、actionはcheck.php、methodはPOSTを指定します。
index.html

+   <form class="login-form" action="check.php" method="POST">
-   <form class="login-form">
        <div class="form-group">
            <p>メールアドレス</p>
            <input type="email" name="email" required>
        </div>
        <div class="form-group">
            <p>パスワード</p>
            <input type="password" name="password" required>
        </div>
        <button class="login-btn"><a href="dashboard.html">ログイン</a></button>
    </form>

check.phpの実装

メールアドレスとパスワードをチェックしてログイン判断を行うファイルを作成します。
adminフォルダにcheck.phpを作成します。

まず、emailとpasswordを受け取ります。
admin/check.php

<?php

    $email = isset($_POST['email'])? htmlspecialchars($_POST['email'], ENT_QUOTES, 'utf-8') : '';
    $password = isset($_POST['password'])? htmlspecialchars($_POST['password'], ENT_QUOTES, 'utf-8'): '';

check.phpはPHPだけで完結するファイルです(HTMLを下に記述したりしないファイル)。
PHPだけで完結するファイルは原則PHPの閉じタグ?>は付けないというのがルールになっていますので、PHPだけで完結するファイルを作成するときは?>を記述しないようにします(記述があっても動きます)。

emailとpasswordは必須なので、どちらについても値がない場合はログイン不可となります。
if文を使って、$email $passwordそれぞれの値が''だったらindex.htmlにリダイレクトを実装します。
admin/check.php

    <?php

        $email = isset($_POST['email'])? htmlspecialchars($_POST['email'], ENT_QUOTES, 'utf-8') : '';
        $password = isset($_POST['password'])? htmlspecialchars($_POST['password'], ENT_QUOTES, 'utf-8'): '';

+       if ($email == '') {
+           header("Location:./index.html");
+           exit;
+       }
+       if ($password == '') {
+           header("Location:./index.html");
+           exit;
+       }

emailとpasswordが正しいかチェックします。
今は、emailは[admin@admin.com]と一致すれば正しい。
passwordは[password01]と一致すれば正しいということにしています。
(以降の章でデータベースを参照する方法も実装します)
admin/check.php

    <?php

        $email = isset($_POST['email'])? htmlspecialchars($_POST['email'], ENT_QUOTES, 'utf-8') : '';
        $password = isset($_POST['password'])? htmlspecialchars($_POST['password'], ENT_QUOTES, 'utf-8'): '';

        if ($email == '') {
            header("Location:./index.html");
            exit;
        }
        if ($password == '') {
            header("Location:./index.html");
            exit;
        }

+       if ($email=='admin@admin.com'&&$password=='password01') {
+           //ログイン許可
+           header("Location:./dashboard.html");
+       } else {
+           //間違っているのでログイン不可
+           header("Location:./index.html");
+           exit;
+       }

確認

emailとpasswordが正しい場合はdashboard.htmlに、間違っている場合はindex.htmlにリダイレクトされるか確認してみます。
admin/にアクセスして確認してみてください。
確認できたらOKです。
※ログインボタンを押さなくてもエンターキーで送信が実行されますが、これはHTMLの仕様です。エンターキーを無効にする制御も可能ですが今回は実装していません。

check.phpでsession保存

現状ではdashborad.htmlアクセスすれば誰でも閲覧できてしまいます。
dashboard.htmlをログインしている人しか閲覧できないようにログイン状態を判断するように実装していきます。
ここで使うのがsessionです。
check.phpでログイン判断して、OKだった場合にログインステータスをsessionに保存します。

session_start()でセッションを開始します。
admin_loginという名前でログイン状態を保存します。
admin/check.php

    <?php

        $email = isset($_POST['email'])? htmlspecialchars($_POST['email'], ENT_QUOTES, 'utf-8') : '';
        $password = isset($_POST['password'])? htmlspecialchars($_POST['password'], ENT_QUOTES, 'utf-8'): '';

        if ($email == '') {
            header("Location:./index.html");
            exit;
        }
        if ($password == '') {
            header("Location:./index.html");
            exit;
        }

        if ($email=='admin@admin.com'&&$password=='password01') {
+           session_start();
+           $_SESSION['admin_login'] = true;
            header("Location:./dashboard.html");
        } else {
            header("Location:./index.html");
            exit;
        }

dashboard.htmlを編集

dashboard.htmlをPHPsessionを扱えるようにphpファイルに変更します。
拡張子をhtmlからphpに変更すればいいです。
拡張子を変更したので、dashboard.htmlにリンクしている箇所はすべてdashboard.phpに変更してください。
<!DOCTYPE html>の上にPHPのsessionの判断機能を実装します。
sessionを開始し、admin_loginがfalseだった場合、index.htmlにリダイレクトさせます。
admin/dashboard.php

+   <?php
+       session_start();
+       if($_SESSION['admin_login'] == false){
+           header("Location:./index.html");
+           exit;
+       }
+   ?>
    <!DOCTYPE html>
    <html>

check.php編集

dashboard.htmlからdashboard.phpに変更になったので、ログインだった場合のリダイレクト先をdashboard.phpに変更します。
admin/check.php

    if ($email=='admin@admin.com'&&$password=='password01') {
        session_start();
        $_SESSION['admin_login']=true;
-       header("Location:./dashboard.html");
+       header("Location:./dashboard.php");
    } else {
        header("Location:./index.html");
        exit;
    }

確認

まず、ログインしていない状態で確認してみます。
admin/dashboard.phpにアクセスしてみてください。
index.htmlにリダイレクトされます。

index.htmlから[admin@admin.com][password01]でログインしてください。
dashboard.phpが表示されたら、一度index.htmlに戻ります。
dashboard.phpに直接アクセスしてください。
dashboard.phpが表示されればOKです。

Sessionファイルの確認

一応、sessionがどのような状態で保存されているのか、ファイルを確認してみましょう。
dashboard.phpをブラウザで開き、デベロッパーツールでcookieを確認してみます。
Networkからdashboard.phpを選択し、Cookiesを確認します。
PHPSESSIDが保存されているのが確認できます。

このセッションIDのファイルを実際に確認してみます。
セッションファイルの保存先はsession_save_path()で確認できます。
確認のために、dashboard.phpに一度echoしてみます。
admin/dashboard.php

    <?php
        session_start();

+       echo session_save_path();

        if($_SESSION['admin_login'] == false){
            header("Location:./index.html");
            exit;
        }

ブラウザでdashboard.phpを確認すると、ヘッダーの上にpathが表示されました。

環境によりますが、私の場合/Applications/MAMP/tmp/phpと表示されました。
確認できたので、echo session_save_path();は削除します。
admin/dashboard.php

    <?php
        session_start();

-       echo session_save_path();

        if($_SESSION['admin_login'] == false){
            header("Location:./index.html");
            exit;
        }

コンソールで表示されたpathまで行き、ls -laでファイル一覧を表示させます。
console

$ cd /Applications/MAMP/tmp/php
$ ls -la

すると、sess_xxxxxxxxx というファイルが確認できます。
xxxxxxxxxxの部分が、先程デベロッパーツールで確認したセッションIDになっているファイルがあるはずです。
ファイルを見つけたら、中身を確認してみます。
console

$ cat sess_xxxxxxxxxxx

すると、

admin_login|b:1;

というようなデータが表示されます。
admin_loginがbool型で1が保存されているということがわかりました。
(bool型は値が0,1です。1なのでtrueということです)

ログアウト機能

sessionに無事保存できて、ダッシュボードのログイン制御も完了しました。
ログインできるようになったので、ログアウトも実装します。
adminフォルダにlogout.phpを作成します。

sessionを開始して、admin_loginをfalseに変更。
index.htmlにリダイレクトさせるだけです。
admin/logout.php

<?php
    session_start();
    $_SESSION['admin_login'] = false;

    header('location:./index.html');

dashboard.phpのログアウトのリンク先をlogout.phpに変更します。
admin/dashboard.php

    <header>
        <div class="container">
            <div class="header-logo">
                <h1><a href="dashboard.html">管理画面</a></h1>
            </div>

            <nav class="menu-right menu">
-              <a href="index.html">ログアウト</a>
+              <a href="logout.php">ログアウト</a>
            </nav>
        </div>
    </header>

確認

  1. ダッシュボードからログアウトでログイン画面にリダイレクト
  2. ダッシュボードに直接アクセスして、ログイン画面にリダイレクト

どちらも想定どおり動けばログアウト機能が実装されました。

sessionファイルを確認

ここで、sessionファイルを確認してみます。
console

$ cd /Applications/MAMP/tmp/php
$ cat sess_xxxxxxxx

admin_loginの値がb:0となっているはずです。
(sessionを完全に削除する場合は、session_destroy();を使えば破棄することができます。)

ここまでで管理画面の認証機能ができました。

変更したファイル名・リンク等

No. ファイル名 内容
1 dashboard.html ファイル名変更:dashboard.phpに変更
2 check.php リダイレクト先変更:dashobard.htmlをdashboard.phpに変更
3 dashboard.php リンク先変更:ログアウトのリンク先をindex.htmlからlogout.phpに変更

コード

https://github.com/bluecode-io/web-basic/tree/basic5-1