39
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Nuxt.js #2Advent Calendar 2018

Day 10

これさえあればWEBアプリが簡単に作れる!Nuxt・Express・Vuetifyを使ったログイン画面の実装テンプレ

Posted at

目的

nuxt.jsを使うことでWEBアプリが簡単に作れるようになりました。
加えてVuetifyやExpressを使うことで、高度なWEBアプリが開発できます。
今回はそれらを使ったログイン画面の実装テンプレートを紹介します。

実装に向けて

https://github.com/nuxt-community
基本このGithubを参考にしました。
Expressやauth-moduleのテンプレートはありますが、
まるっと統合したものが欲しかったので、これらを応用して作成しました。

使用したフレームワーク・ライブラリ

  • nuxt
  • Express(API)
  • vuetify(デザイン)
  • axios(API通信)

ドキュメント

GitHubレポジトリ

ソース解説

フォルダ構成

./api                  # Express
./api/index.js
./api/routes           # Express Router
./api/routes/auth.js   # 認証用API
./assets
./assets/css
./assets/css/main.css  # 全体のCSS
./components
./layouts              # レイアウト
./layouts/default.vue  # デフォルトのレイアウト(Vuetify)
./layouts/error.vue
./layouts/blank.vue    # ログイン画面用のレイアウト(ログイン画面ではヘッダーとかは見えてほしくないので)
./nuxt.config.js       # nuxtの設定
./package.json
./pages                # この中に各ページを作る
./pages/index.vue      # ホーム画面
./pages/login.vue      # ログイン画面
./plugins
./static
./static/favicon.ico
./store
./store/index.js

nuxt.config.js

nuxt.config.js}
module.exports = {
    // <head>タグの中身
    head: {
        title: 'nuxt-login-template',
        meta: [
            { charset: 'utf-8' },
            { name: 'viewport', content: 'width=device-width, initial-scale=1' },
            { hid: 'description', name: 'description', content: 'nuxt login template' }
        ],
        link: [
            { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
            {
                rel: 'stylesheet',
                type: 'text/css',
                href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons'
            }
        ]
    },
    plugins: [],
    css: [
        // 全体のCSS
        '~/assets/css/main.css'
    ],
    build: {
        vendor: ['@nuxtjs/axios'],
        extractCSS: true,
        extend(config) {
            if (process.server && process.browser) {
                config.module.rules.push({
                    enforce: 'pre',
                    test: /\.(js|vue)$/,
                    loader: 'eslint-loader',
                    exclude: /(node_modules)/
                });
            }

        }
    },
    serverMiddleware: [
        // API
        '~/api/index.js'
    ],
    // ライブラリの読込
    modules: [
        '@nuxtjs/axios',
        '@nuxtjs/auth',
        '@nuxtjs/vuetify'
    ],
    axios: {
        proxy: true
    },
    proxy: {
        '/api': `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}`
    },
    // ここでauth-moduleを有効にする
    auth: {
        redirect: {
            login: '/login',
            logout: '/',
            callback: '/login',
            home: '/'
        },
        strategies: {
            local: {
                endpoints: {
                    login: {
                        url: '/api/auth/login',
                        method: 'post',
                        propertyName: 'token.accessToken'
                    }
                }
            }
        }
    },
    // Vuetifyを有効にする
    vuetify: {}
};

api

Expressを使って実装。
認証はexpress-jwtを使用。

index.js

index.js}
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const jwt = require('express-jwt');

const app = express();
const secret = 'dummy';  // jwt secret

app.use(cookieParser());
app.use(bodyParser.json());

// ログインしてなくてもここにはアクセスできる
app.use(
    jwt({ secret })
        .unless({ path: '/api/auth/login' })
);

app.use('/auth', require('./routes/auth'));

// other routes

app.use((err, req, res, next) => {
    console.error(err);
    res.status(401).send(err + '');
});

module.exports = {
    path: '/api',
    handler: app
};

routes/auth.js

auth.js}
const { Router } = require('express');
const jsonwebtoken = require('jsonwebtoken');

const router = Router();
const secret = 'dummy';

async function checkAuth({ username, password }) {

    // ここでDBに接続するなり、IDPASSの確認

    if (username === 'xxx' && password === 'xxx') {
        return true;
    }

    return false;

}


router.post('/login', async (req, res, next) => {

    const { username, password } = req.body;

    if (!await checkAuth({ username, password })) {
        res.status(401).send('Invalid username or password');
        return;
    }

    const accessToken = jsonwebtoken.sign({
        username
    }, secret);

    res.json({ token: { accessToken } });

});

router.get('/user', (req, res, next) => {
    res.json({ user: req.user });
});

router.post('/logout', (req, res, next) => {
    res.json({ status: 'OK' });
});

module.exports = router;

pages

index.vue

index.vue}
<template>
    <v-container fluid>
        index
    </v-container>
</template>

<script>

export default {
    // middlewareにauthを追加することで、
    // ログインしていないとログイン画面にリダイレクトされる
    middleware: ['auth'],
    head() {
        return {
            title: 'ホーム'
        };
    }
};
</script>

<style scoped>
</style>

login.vue

login.vue}
<template>
    <v-container>
        <v-layout justify-center>

            <v-flex xs12 sm4>

                <v-toolbar color="primary" dark>
                    <v-toolbar-title>ログイン</v-toolbar-title>
                </v-toolbar>

                <v-card>
                    <v-container fluid>
                        <v-form ref="form" v-model="valid" lazy-validation>

                            <v-layout row wrap>
                                <v-flex xs12>
                                    <v-alert v-if="error" :value="true" type="error" outline>
                                        {{ error }}
                                    </v-alert>
                                </v-flex>
                            </v-layout>

                            <v-layout row wrap>
                                <v-text-field type="text" v-model="username" label="ログイン名" required></v-text-field>
                            </v-layout>

                            <v-layout row wrap>
                                <v-text-field type="password" v-model="password" label="パスワード" required></v-text-field>
                            </v-layout>

                            <v-layout row wrap justify-end>
                                <v-btn color="success" :disabled="!valid" @click="login">ログイン</v-btn>
                            </v-layout>

                        </v-form>
                    </v-container>
                </v-card>

            </v-flex>

        </v-layout>
    </v-container>
</template>

<style scoped>
</style>

<script>

export default {
    layout: 'blank', // layouts/blank.vueを使用
    middleware: ['auth'],
    head() {
        return {
            title: 'ログイン'
        }
    },
    data() {
        return {
            valid: true,
            username: '',
            password: '',
            error: null
        };
    },
    methods: {

        async login() {

            this.error = null;

            if (this.$refs.form.validate()) {

                return this.$auth
                    .loginWith('local', {
                        data: {
                            username: this.username,
                            password: this.password
                        }
                    })
                    .catch(e => {
                        this.error = 'ログインに失敗しました。IDかパスワードが間違っている可能性があります。';
                    });

            }
        }
    }
}
</script>

使い方

ソースのダウンロード
$ git clone git://github.com/tonio0720/nuxt_login_template.git .

依存のインストール
$ npm i

開発環境に実装
$ npm run dev

※username:xxx、password:xxxでログインできます。(api/routes/auth.js)
login.PNG

最後に

いかがでしたでしょうか。
不明点等あれば、コメントにお願いいたします!

39
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?