５. Koa2でWebページを作成しよう　〜アカウント登録〜

## 記事一覧
* [第一回 Koa2の環境を構築する](https://qiita.com/MEDICE-NOTE/items/0c0d18cf01bc4ae50c05)
* [第二回 Hello World!! を表示する](https://qiita.com/MEDICE-NOTE/items/6260a7a58bc0c3303de0)
* [第三回 koa-routerでGETパラメーターを取得する](https://qiita.com/MEDICE-NOTE/items/bb363d091bff59345da7) 
* [第四回 koa-bodyでPOSTパラメーターを取得する](https://qiita.com/MEDICE-NOTE/items/b630748133c954a50ec1) 

## 概要
今回は、アカウント登録、ログイン検証の実装を行います。
当記事ではログインは検証に留め、ログイン状態の保持は後の記事で進めていく予定です。
また、前回の続きとして進めて行くので、上手くいかない場合は以前の記事をご覧ください。

アカウントの登録やログインに必要なデータベースはMariaDB(MySQL)を使用します。

## インストール
こちらのページからXAMPPをダウンロードし、インストールしてください。
https://www.apachefriends.org/jp/download.html

インストールが完了したら、MySQL DatabaseとApache Web Serverを起動してください。
ブラウザから以下のURLにアクセスすることで、MariaDBに接続・操作できます。
http://localhost/phpmyadmin/index.php

### エラーが発生した場合
> preg_match_all(): Allocation of JIT memory failed, PCRE JIT will be disabled. This is likely caused by security restrictions. Either grant PHP permission to allocate executable memory, or set pcre.jit=0

というエラーが表示された場合は、以下の手順を試してください。

XAMPPをインストールしたディレクトリから、**/xamppfiles/etc/php.ini**を開く。
[Pcre]の文字列で検索して、その次の行に以下の記述を追加。
`pcre.jit=0`

## データベースの作成
今回使用するデータベースを作成します
以下の記述をphpMyAdminのSQLタブのテキストボックスに追加して実行してください。

```sql
CREATE DATABASE koa2_test;
USE koa2_test;
CREATE TABLE koa2_test.user
(
    user_id   INT AUTO_INCREMENT PRIMARY KEY,
    user_name VARCHAR(20)  NOT NULL,
    password  VARCHAR(255) NOT NULL
);
```

画面左のリストからkoa2_testデータベースをクリックすると、作成したテーブルが確認できます。

Koaでデータベースに接続する処理を記載します。
接続するにあたって、こちらのパッケージを利用します。
https://github.com/sidorares/node-mysql2

以下のコマンドをコンソールで実行してください。
`npm install mysql2`

DB接続にはコネクションプールを作成し、これをすべてのデータベースへのクエリで利用します。
`/app/db.js` を作成し、以下の記述を追加してください。

```db.js
const mysql = require('mysql2/promise');
const config = require('../config.json');

const connection = mysql.createPool(config.db);

module.exports = connection;
```

DB接続を行う外部ファイルでは、当ファイルをrequireで読み込むことでコネクションプールを利用します。

また、設定情報を保存するためのファイルを作成します。
`/config.json`を作成し、以下の記述を追加してください。

```config.json
{
  "db": {
    "host": "localhost",
    "port": 3306,
    "user": "root",
    "password": "",
    "database": "koa2_test"
  }
}
```

設定ファイルを別に用意することで、設定情報を一元管理できたり、Gitにパスワード等の情報が保存されてしまう事を防ぐことができるので、このようにすると良いようです。

## アカウント登録
`/view/signup.ejs`を作成し、以下の記述を追加してください。

```signup.ejs
<h1>アカウント登録</h1>
<form action="signup" method="post">
   USER NAME <input type="text" name="name"><br>
   PASSWORD <input type="password" name="password"><br>
   <input type="submit" value="登録"><br>
</form>
```

`/router/signup.js`を作成し、以下の記述を追加してください。

```signup.js
const Router = require('koa-router');
const router = new Router();
const connection = require('../app/db');

router.get('/signup', async (ctx) => {
    await ctx.render('signup');
});

router.post('/signup', async (ctx) => {
    // POSTパラメータを取得
    let userName = ctx.request.body['name'];
    let password = ctx.request.body['password'];

    // DBにアカウント情報を登録
    // SQL文中の ? はプレースホルダーというもので、ここに後から値を割り当て(バインド)します。
    // ここでは、insert文のuser_nameとpasswordに当たる箇所を ? としており、
    // query()関数の第二引数として配列の形で、バインド値を渡しています。
    let signupSQL = 'INSERT INTO user (user_name, password) VALUES (?, ?)';
    await connection.query(signupSQL, [userName, password]);

    await ctx.render('signup');
});

module.exports = router;
```

`/index.js`に以下の記述を追加してください。

```index.js
const signupRouter = require('./router/signup');
app.use(signupRouter.routes());
app.use(signupRouter.allowedMethods());
```

### 動作を確認
ブラウザで http://localhost:5000/signup にアクセスすると、アカウント登録ページが表示されます。
http://localhost/phpmyadmin/ で情報がDBに登録されていることが確認できます。

### パスワードのハッシュ化
現在の状態ではパスワードが平文で登録されてしまっているので、ハッシュ化して登録するように変更します。
パスワードのハッシュ化にはこちらのパッケージを利用します。
https://github.com/kelektiv/node.bcrypt.js

以下のコマンドをコンソールで実行してください。
`npm install bcrypt`

`/router/signup.js`を以下のように修正してください。

```signup.js
const Router = require('koa-router');
const router = new Router();
const connection = require('../app/db');
const bcrypt = require('bcrypt');

router.get('/signup', async (ctx) => {
    await ctx.render('signup');
});

router.post('/signup', async (ctx) => {
    // POSTパラメータを取得
    let userName = ctx.request.body['name'];
    let password = ctx.request.body['password'];

    //パスワードをハッシュ化
    const salt = bcrypt.genSaltSync();
    const hashPassword = bcrypt.hashSync(password, salt);

    // DBにアカウント情報を登録
    let signupSQL = 'INSERT INTO user VALUES (0, ?, ?)';
    await connection.query(signupSQL, [userName, hashPassword]);

    await ctx.render('signup');
});

module.exports = router;
```

この状態で再びアカウント登録を実行すると、以下のような文字列がパスワードとして保存されます。
「$2b$10$pO2kkkS0FWpKMgIoCg5QbuQilL7E3nbWF6im47pvSx8…」

以上で新規登録は完了です。

## ログイン
`/view/login.ejs`を作成し、以下の記述を追加してください。

```login.ejs
<h1>ログイン</h1>
<form action="login" method="post">
    USER NAME <input type="text" name="name" placeholder="userと入力"><br>
    PASSWORD <input type="password" name="password" placeholder="userと入力"><br>
    <input type="submit" value="ログイン"><br>
</form>

<a href="signup">アカウント登録</a>

<!-- この部分でログイン結果を表示するようにjs側で処理を書いていきます。 -->
<% if(typeof loginResult !== "undefined"){ %>
    <br>
    <%= loginResult %>
<% } %>
```

`/router/login.js`を作成し、以下の記述を追加してください。

```login.js
const Router = require('koa-router');
const router = new Router();
const connection = require('../app/db');
const bcrypt = require('bcrypt')

router.get('/login', async (ctx) => {
    await ctx.render('login');
});

router.post('/login', async (ctx) => {
    // POSTパラメータを取得
    let userName = ctx.request.body['name'];
    let password = ctx.request.body['password'];

    // DBからuserNameが一致するデータを検索
    let loginSQL = 'SELECT user_id, password FROM user WHERE user_name = ?';
    let [queryResult] = await connection.query(loginSQL, [userName]);

    // 入力されたパスワードとDBに保存されているハッシュ化されたパスワードを比較
    let loginResult = 'ログイン失敗';
    for (let row of queryResult) {
        if (bcrypt.compareSync(password, row['password'])) {
            loginResult = 'ログイン成功';
        }
    }

    await ctx.render('login', {loginResult: loginResult});
});

module.exports = router;
```

作成したrouterを読み込む処理をindex.jsに追加します。
`/index.js`に以下のコードを追加してください。
 
```index.js
const loginRouter = require('./router/login');
app.use(loginRouter.routes());
app.use(loginRouter.allowedMethods());
```

### 動作を確認
ブラウザでhttp://localhost:5000/login にアクセスすると、ログイン画面が表示されます。
!# 画像を挿入

## まとめ
以上で新規登録とログインの検証が完了です。
次回は入力値のバリデーションを書いていく予定です。