Express.jsとMySQLを使って認証機能を実装する
expressでは、passportの認証機能を利用するのが便利です。 twitterやfacebookなどとも連携できます。 ここではローカルでの認証について、アウトプットとしてまとめました。
expressアプリの作成
# terminal $ npm install -g express-generator $ express --view=pug your-app-name $ cd your-app-name $ npm install
以降、
# terminal $ npm start
でアプリケーションを起動させます。 http://localhost:3000
必要項目のインストール
# terminal $ npm install --save passport $ npm install --save passport-local $ npm install --save express-session
ログイン実装
// 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の文字が出てきます。
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を書く順番がおかしくなっていないかとか、誤字脱字等がないかどうかとか、チェックしていってください。
MySQLのユーザー情報を基にログイン
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です。 いい感じのところにサインアウトボタンを配置してあげてください。