expressでユーザー管理+MySQL


expressでは、passportの認証機能を利用するのが便利です。
twitterやfacebookなどとも連携できます。
ここではローカルでの認証について、アウトプットとしてまとめました。

<h1>expressアプリの作成</h1>
```terminal
$ npm install -g express-generator
$ express --view=pug your-app-name
$ cd your-app-name
$ npm install
```

以降、

```terminal
$ npm start
```

でアプリケーションを起動させます。
http://localhost:3000

<h1>必要項目のインストール</h1>
```terminal
$ npm install --save passport
$ npm install --save passport-local
$ npm install --save express-session
```

<h1>ログイン実装</h1>
```app.js
// 既に色々入っているので、そこへ追加
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var session = require('express-session');

// signinページの追加
var signinRouter = require('.routes/signin');
app.use('/signin');

// session, passport.initialize, passport.sessionは以下の順番で追加
app.use(session({
  secret: "testing",
  resave: false,
  saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());

// authentication
passport.serializeUser(function(username, done) {
  console.log('serializeUser');
  done(null, username);
});

passport.deserializeUser(function(username, done) {
  console.log('deserializeUser');
  done(null, {name:username});
});

passport.use(new LocalStrategy(
  {
    // signinのformで定義したnameの要素をセット
    usernameField: "username",
    passwordField: "password"
  },
  function(username, password, done){
    // ここでは、データベースを使わずに、仮にusernameとpasswordを固定で入れています。
    if(username == "test" && password == 123456789){
      return done(null, username);
    }
    return done(null, false, {message: "invalid"});
  }
));

app.post('/signin',
  passport.authenticate('local',
    {
      failureRedirect: "/signin"
    }
  ),
  function(req, res, next){
    // res.redirect("/")でreq.userが渡せなかったので、ここでfetchを使っています。
    // https://github.com/jaredhanson/passport/issues/244
    // fetchは以下のようにインストール
    // npm install --save isomorphic-fetch
    // var fetch = require('isomorphic-fetch');
    fetch("http://localhost:3000/signin",
      {
        credentials: "include"
      }
    ).then(function(){
      res.redirect("/");
    }).catch(function(e){
      console.log(e);
    });
  }
);
```

で、index.pugへリダイレクトした時に、そのページ上にログインしたユーザーの名前を表示します。

```index.js
var express = require('express');
var router = express.Router();

// index.pugをgetした時にコールバックを実行
router.get('/', function(req, res, next) {
  console.log(req.user); // {name:'test'}
  if (req.user) { // ログインしているユーザーが存在する場合のみ有効
    res.render("index", { username: req.user.name});
  } else { // ユーザーが存在しなければ、サインインページへ飛ばされる
    res.redirect('/signin');
  }
});

module.exports = router;
```

```index.pug
extends layout

block content
  h1= username
  p Welcome to Express
```

こんな感じでtestの文字が出てきます。
<img width="462" alt="Screen Shot 2018-07-18 at 23.15.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/245704/057592b3-89f7-3921-34aa-ed724e5f0b1e.png">

signin.jsはindex.jsをコピペしてsignin用に書き換えるだけで良いです。

```signin.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
  res.render("signin", {});
});

module.exports = router;
```

一応signin.pugも

```signin.pug
extends layout

block content
  form(action="/signin" method="post")
    .username
      label username
      input(type="text" name="username") // nameはnew LocalStrategyのところで使います。
    .password
      label password
      input(type="password" name="password") // nameはnew LocalStrategyのところで使います。
    .signin
      input(type="submit")
```

ログイン時にconsole.log('serializeUser')とconsole.log('deserializeUser')が実行されていることを確認しましょう。
serializeUserだけがコンソールで出力されていてもdeserializeUserが出力されていなければ、ログインは成功していません。
その時は、app.use時のsession、passport.initilizer、passport.sessionを書く順番がおかしくなっていないかとか、誤字脱字等がないかどうかとか、チェックしていってください。

<h1>MySQLのユーザー情報を基にログイン</h1>
mysqlで適当にデータベースを作ります。

```terminal
mysql> create table users (id int not null primary key, username varchar(20), password int);
```

とかにして、

```terminal
mysql> insert into users values (1, 'test', 123456789);
```

みたいにデータを入れておきます。
次にapp.jsでmysqlを使い、データを取り出してログイン時に入力された値と照合させるようにします。

```app.js
// npm install --save mysql2
var mysql = require('mysql2');
var connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'your-password', // mysqlの自分のパスワード
  database: 'testdb' // db名は自分で自由に作った名前を当てはめる
});

// 上で書き込んだpassport.useの部分を書き換えます。
passport.use(new LocalStrategy(
  {
    usernameField: "username",
    passwordField: "password"
  },
  function(username, password, done){
    connection.query("select * from users;", function(err, users) {
      // usernameもpasswordもユニーク前提
      var usernames = [];
      var passwords = [];
      for (i = 0; i < users.length; i++) {
        usernames.push(users[i].username);
        // input(type="password")で渡される値はstringのようなので、
        // データベースから取り出した値もstringにしています。
        var pw = users[i].password.toString();
        passwords.push(pw);
      }
      if (usernames.includes(username) && passwords.includes(password)) {
        return done(null, username);
      }
      return done(null, false, {message: "invalid"});
    });
  }
));
```

これで、ログイン時に
ユーザー名： test
パスワード： 123456789
を入れてあげると、ログインできるようになります。

サインアップは、サインアップのフォームを作って、post時に/signupへやってきた時のコールバックにロジックを書いてあげれば良いです。

```app.js
app.post('/signup', function(req, res, next){
  connection.query('select * from users;', function(err, users){
    connection.query('insert into users set ? ;', {
        username: req.body.username,
        email: req.body.email,
        password: req.body.password,
        created_at: new Date(),
        updated_at: new Date()
      },
      function(err){
        console.log("サインアップに関するエラー： " + err);
        res.redirect("/signin");
      }
    );
  });
});
```

まあ、こんな感じで。

サインアウトは、

```app.js
app.get('/signout', function(req, res, next) {
  req.logout();
  console.log('ログアウトしました');
  res.redirect('/');
});
```

これだけでOKです。
いい感じのところにサインアウトボタンを配置してあげてください。