Developer

魁!小野の塾 小さな管理機能を作ってみようの巻 第21話
2021.09.23
Lv2

魁!小野の塾 小さな管理機能を作ってみようの巻 第21話

小さな管理機能を作ってみよう 第21話

初心者向け、PHPプログラム構築講座です。
初心者といっても、PHPの勉強を少し行い、LAMP環境が自分で構築でき、少しアプリケーションを作成しているレベルを対象とします。
まったくの初心者の場合は、わからない部分が出てくると思います。
できるだけ細かく説明は入れていきますが、説明がわからない場合は、PHPやMySQLの初心者講座をご覧ください。

対象のスキルレベル

  • LAMP環境の構築
  • PHP言語が読める
  • HTML, CSS, Javascriptが少しわかる
  • Bootstrapのドキュメントをみて、HTMLが書ける
  • Ajax(非同期通信)を利用したことがある
  • SESSIONを利用したことがある

構築環境

  • Windows10
  • XAMPP(PHP7.3.2, MariaDB 10.1.38)

ログイン機能の実装

ユーザの登録機能実装が終了しましたので、ログイン機能を実装していきたいと思います。
ユーザ登録機能を使って、ログインするユーザを作成しておいてください。(ログインできないですからね)

まずは、HTMLを修正後に、Auth.phpを作成し、最後にJavascriptを実装していきます。
ユーザ登録の実装を行ったので、metaタグが必要だということがお分かりかと思いますが、processとmethodを保持するmetaタグの追加、各入力項目にname属性の追加の2つをHTMLに行っていきます。
ユーザ登録同様にmetaタグを追加していきます。

まずは、metaタグを追加します。

login.html(抜粋)
<!doctype html>
<html lang="ja" >
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
	<meta name="process">
	<meta name="method">
	<title>LOGIN | tech.pjin.jp</title>

	<!-- Bootstrap CSS -->
	<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
	<!-- Font Awesome CSS -->
	<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
	<!-- Animate.css CSS -->
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
	<!-- Sawarabi Gothic -->
	<link href="https://fonts.googleapis.com/css?family=Sawarabi+Gothic" rel="stylesheet">

	<link rel="stylesheet" href="assets/css/style.css" />
</head>

続いて、ログイン画面のname属性を追加していきます。

login.html(抜粋)
<main class="form-signin shadow bg-white rounded needs-validation" novalidate>
	<form>
		<h1 class="mb-5"><i class="fas fa-paste"></i> BACK<span class="bland-red">OFFICE</span></h1>

		<div class="error"></div>

		<div class="form-floating">
			<input type="email" id="mail" name="mail" class="form-control" placeholder="メールアドレス" required="" autofocus="">
			<label for="mail">メールアドレス</label>
		</div>
		<div class="form-floating">
			<input type="password" id="passwd" name="passwd" class="form-control" placeholder="パスワード" required="">
			<label for="passwd">パスワード</label>
		</div>

		<div class="checkbox mb-3">
			<label>
				<input type="checkbox" id="remember-me"> 記憶する
			</label>
		</div>
		<button class="w-100 btn btn-lg btn-success signin" type="submit"><i class="fas fa-sign-in-alt"></i> ログイン</button>
		<p class="mt-5 mb-3 text-muted">&copy; 2021 SIE</p>
	</form>
</main>

Javascriptの実装前にControllerを作成していきたいと思います。

認証用Controller実装

Controllerは api フォルダー内に実装します。index.phpがリクエストを受け、Controllerにprocessを渡すと、指定ファイルを読込、methodで実行する関数を決定します。
認証機能を実装しますので、Auth.php を実装していきます。実装したいものは、ユーザ情報取得、ログイン、ログアウト、ログインチェックの4機能になります。

まず、apiフォルダーに、Auth.php を作成してください。

/api/Auth.php
<?php
class Auth {
	/* traitを使う宣言 */
	use Result;

	/**
	 * ログインチェック jsonでログインしているかを返します
	 * 記憶するにチェックがある場合は、reパラメータに入っている値を検証します。
	 */
	function isLogin($con, $request){
		/* sessionを確認 */
		if(isset($_SESSION['login'])){
			/* あれば成功 */
			$this->success();
			/* ユーザ情報をセットして返します */
			$this->setList('login', $_SESSION['login']);
		}else{
			/* ない場合は、記憶するにチェックがある場合に送信されるパラメータ(re)をチェック */
			if(is_null($request['re'])){
				/* nullの場合は、未ログインとして返します */
				$this->error('success', '*', '未ログイン');
			}else{
				/* ある場合は、メールアドレスが入っているので、ユーザ情報をデータベースから取得 */
				$user	= $this->getUser($con, $request['re']);
				/* ユーザ情報がnullでなければ、ログイン成功とみなします */
				if(!is_null($user)){
					/* sessionに情報を入れる前にパスワードハッシュを抜いておきます */
					unset($user['hash']);
					/* sessionにログイン情報を保存します */
					$_SESSION['login']	= $user;
					/* session_idを再付番します */
					session_regenerate_id();
					/* 成功をセットし、ユーザ情報を返します */
					$this->success();
					$this->setList('login', $_SESSION['login']);
				}else{
					/* ユーザが無ければ未ログイン */
					$this->error('success', '*', '未ログイン');
				}
			}
		}
		return $this->result();
	}

	/* ログアウト処理 */
	function logout(){
		/* session_idを再付番 */
		session_regenerate_id();
		/* sessionからログイン情報を破棄 */
		unset($_SESSION['login']);
		/* ログアウト成功を通知 */
		$this->success();
		return $this->result();
	}

	/* ログイン処理 */
	function login($con, $request){
		/* 送信されたメールアドレスよりユーザ情報を取得 */
		$rs	= $this->getUser($con, $request['mail']);
		if(!is_null($rs)){
			/* ユーザ情報が取得できたら、パスワードを検証 */
			if(password_verify($request['passwd'], $rs['hash'])){
				/* sesssionに登録するために、パスワードハッシュは抜いておきます */
				unset($rs['hash']);
				/* ユーザ情報を保持 */
				$_SESSION['login']	= $rs;
				/* session_idを再付番します */
				session_regenerate_id();
				$this->success();
			}else{
				$this->error('error', 'mail', 'ユーザ又はパスワードが異なります。');
			}
		}else{
			$this->error('error', 'mail', 'ユーザ又はパスワードが異なります。');
		}
		return $this->result();
	}

	/* ユーザ情報をメールアドレスから取得します */
	function getUser($con, $mail){
		$sql	= "select * from users where mail = ?";
		$rs		= null;
		if($stmt = $con->prepare($sql)){
			/* プレースフォルダーに値をセットします */
			$stmt->bind_param("s", $mail);
			/* SQLの実行 */
			$stmt->execute();
			$row	= [];
			/* fetchする際のデータの割り当てを設定します */
			$stmt->bind_result(
				$row['user_id'],
				$row['name'],
				$row['mail'],
				$row['tel'],
				$row['addr'],
				$row['dept_id'],
				$row['hash'],
				$row['created'],
				$row['modified']
			);
			/* 最初の一行だけ取得するので、whileではなくifにしています */
			if($stmt->fetch()){
				/* fetchが完了したら変数に割り当てます */
				$rs	= $row;
			}
			$stmt->close();
		}
		return $rs;
	}
}
?>

実装機能は全部で4機能になります。

  1. isLogin($con, $request)
  2. logout()
  3. login($con, $request)
  4. getUser($con, $mail)

isLogin では、ログインしているかの状況を返します。ログイン出来ていない(sessionの有効期限が切れている場合)に記憶するにチェックがある場合に自動ログインできる仕掛けの実装も含みます。各機能を実行する最初にログインしているかのチェックを行う際に利用します。

logout では、sessionからユーザ情報を破棄し、session_idを再付番します。これはセッションハイジャック などのセキュリティー対策です。cookieを盗まれるとそのまま悪用できたりしますので、覚えておきましょう。

login では、ユーザ情報を取得し、パスワードを検証、問題なければsessionにログイン情報を保持、session_id再付番、ログイン完了を通知の流れになります。画面からの入力を想定しているので、エラーがある場合は、エラー情報を返します。

getUser では、メールアドレスからユーザ情報を返します。同一のメールアドレスが登録されている場合は、最初に取得した1件のみ返却します。他の関数で利用するものなので、privateで良いと思います。

※参考 安全なウェブサイトの作り方 – 1.4 セッション管理の不備
※参考 セッションハイジャックとは?セッションIDを利用したなりすまし
※参考 セッションID固定化攻撃とは?仕組みから対策方法まで徹底解説

安全なシステムを構築するには必要な情報がかなりありますので、一通り読みながら、どのように実装していくか考える癖をつけましょう。

今回はこの辺りで、次回からJavascript側の実装を行っていきたいと思います。

魁!小野の塾