node.js でログインページを作成する

  • 10
    Like
  • 0
    Comment

目的

下図のような簡易ログインページを作成することを目的とします。
図1.png

WEBサーバは node.js の Express 、登録用ページは react、登録済みユーザ管理DB には PostgreSQL を使用しました。
上図ではユーザ・WEBサーバ間でHTTPS通信をしていますが、本記事ではHTTP通信をしています。

WEBサーバ構築

node.js の Express を使用します。
まずは localhost:3000 に Express のデフォルトページを表示します。

command
mkdir tmp
cd tmp/
npm install express --save
npm install experss-generator --save
./node_modules/.bin/express
npm install
npm start

image.png

表示できました。

WEBページ構築

react を使用して構築します。
Express では localhost:3000 にアクセスした際、public/index.html がある場合は index.html を表示します。
public/ フォルダ以下に index.html を、public/javascripts/ フォルダ以下に login.jsx を設置します。
それぞれのファイルの中身を以下のように編集します。

index.html
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="https://fb.me/react-0.13.3.js"></script>
    <script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
    <script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
</head>
<body id="body">
    <script type="text/jsx" src="javascripts/login.jsx">
    </script>
</body>
</html>
login.jsx
const LoginForm = React.createClass({
    getInitialState: function () {
        return { userID: "", password: "", message: "" };
    },
    updateUserID: function (e) {
        this.setState({ userID: e.target.value });
    },
    updatePassword: function (e) {
        this.setState({ password: e.target.value });
    },
    login: function (e) {
        e.preventDefault();
        const body = { userID: this.state.userID, password: this.state.password };

        $.ajax({
            url: "/api/login",
            dataType: 'json',
            type: 'POST',
            data: body
        })
        .then(
            function (data) {
                if (data.authorized) {
                    this.setState({ message: "Login Succeeded." });
                }
                else {
                    this.setState({ message: "Login Failed." });
                }
            }.bind(this),
            function () {
                console.error("Error");
                console.error(this.props.url);
                console.error(body);
            }.bind(this)
        );
    },
    render: function () {
        return (
            <article>
                <h1>Test page.</h1>
                <form onSubmit={this.login}>
                    <table>
                        <tbody>
                            <tr>
                                <td>User ID: </td>
                                <td><input type="text" placeholder="user id" value={this.state.userID} onChange={this.updateUserID} /></td>
                            </tr>
                            <tr>
                                <td>Password: </td>
                                <td><input type="password" placeholder="password" value={this.state.password} onChange={this.updatePassword} /></td>
                            </tr>
                        </tbody>
                    </table>
                    <input type="submit" value="Login" />
                </form>
                <p>{this.state.message}</p>
            </article>
        );
    }
});

React.render(
    <LoginForm />,
    document.getElementById('body')
);

上記の index.html と login.jsx で表示されるページは以下になります。

image.png

User ID と Password 、それぞれの文字列を json にして /api/login に POST し、結果を受信するプログラムとなっています。

WEB API (login) 作成

app.js を編集して作成します。
処理の流れは、

  1. User ID と Password を受信し、Password をハッシュ化
  2. 登録済みユーザ DB で同一の User ID を持つレコードを取得
  3. ハッシュ化した Password と、取得したレコードの user_password_hash フィールドが一致していればログイン成功。異なるならログイン失敗

となります。

新しく bcryptjs と pg が必要になるので

command
npm install bcryptjs --save
npm install pg --save

でインストールしてください。

app.js
.
.
.
var bcrypt = require('bcryptjs');
var pg = require('pg')
.
.
.
app.use('/users', users);

app.post('/api/login', function (req, res) {
    bcrypt.hash(req.body.password, 10, function (err, hash) {
        pg.connect("http://postgres:test@localhost:5432/postgres", function (err, client) {
            var query = client.query("SELECT * FROM REGISTERED_USERS WHERE USER_NAME = $1;", [req.body.userID]);

            var userInfo = null;
            query.on('row', function (row) {
                // USER_NAME がユニークな前提
                userInfo = row;
            });
            query.on('end', function (row, err) {
                if (userInfo) {
                    bcrypt.compare(req.body.password, userInfo.user_password_hash, function (err, r) {
                        res.send({ authorized: r });
                    });
                }
                else {
                    res.send({ authorized: false });
                }
            });
        });
    });
});

// catch 404 and forward to error handler
.
.
.

登録済みユーザ DB 構築

PostgreSQL を使用して登録済みユーザ DB を構築します。
テーブルの定義を以下に示します。

  • REGISTERED_USES テーブル
修飾語
user_name (PRIMARY KEY) character varying(64) not null
user_password_hash character varying(64) not null

REGISTERED_USERS に挿入するレコードは以下になります。

user_name user_password_hash
isishizuka $2a$10$XurU33aLFQEAT5591IlGjuZrJ.W.yOU2mIRsdcgQ23ds2KxJ/i0I.

user_password_hash は、"test" 文字列を bcrypt.hash で予め生成したハッシュを使用しています。
テーブル定義とレコード挿入のコマンドを以下に示します。

command
CREATE TABLE REGISTERED_USERS( USER_NAME VARCHAR(64) PRIMARY KEY, USER_PASSWORD_HASH VARCHAR(64) NOT NULL );
INSERT INTO REGISTERED_USERS VALUES ( 'isishizuka', '$2a$10$XurU33aLFQEAT5591IlGjuZrJ.W.yOU2mIRsdcgQ23ds2KxJ/i0I.' )

動作

実際に動かしてみます

USER ID Password
isishizuka test

で Login ボタンを押すと

image.png

となり、ログインに成功します。
上記以外の組み合わせでは

image.png

となり、ログインに失敗します。

参考サイト