Firestoreで簡易的なログインを実装する
FirebaseのFirestoreを使用してログイン機能を実装します。
例
・ログイン機能をつけたいが、何らかの理由でAuthenticationを使えない場合
(メアドが使えない、googleアカウントを持っていないなど)
実装について
・Nuxt.js(v2.14.5)、Node.js(v13.14.0)、firebase(v8.2.6)を使用
・Firestoreでログイン情報を管理する
・firebaseの情報は/pluginsに記載する
・vuex-persistedstateを使用し、localstrageでログイン情報を保存することでリロードも対応する
・1回目ログインで新規登録、2回目以降のログインではデータベース(以下"DB"と記載)、またはlocalstrageよりデータを取得する
・middlewareを使用し、ログイン情報がない場合は"/"にリダイレクトさせる(情報がある場合は"/page01"にリダイレクト)
※Nuxt.js、Firestoreの連携や設定は省略
コード
<template>
<div class="container">
<div class="container__wrap">
<div class="container__info">
<div class="container__info--block">
<div>
ユーザID<br />
<p class="container__info--text">(半角英数字)</p>
</div>
<input type="text" v-model="user.number" class="container__info--number" placeholder="000000" oncopy="return false" onpaste="return false" @keyup="inputCheck" />
</div>
<div class="container__info--block">
<div>
ユーザID(確認用)<br />
<p class="container__info--text">(半角英数字)</p>
</div>
<input type="text" class="container__info--confirm" placeholder="000000" oncopy="return false" onpaste="return false" @keyup="inputCheck" />
</div>
<div class="container__info--block">氏名<input type="text" v-model="user.name" class="container__info--name" placeholder="鈴木 太郎" @keyup="inputCheck" /></div>
</div>
<button type="submit" @click="login" disabled class="container__wrap--start">ログイン</button>
</div>
</div>
</template>
<script>
import firebase from '@/plugins/firebase';
const db = firebase.firestore();
const usersCollection = db.collection('users');
export default {
data() {
return {
user: {
name: '',
number: '',
},
};
},
methods: {
inputCheck() {
//ユーザIDと名前の空欄チャック
const loginButton = document.querySelector('.container__wrap--start');
const inputValueNumber = document.querySelector('.container__info--number').value;
const inputValueName = document.querySelector('.container__info--name').value;
const inputValueConfirm = document.querySelector('.container__info--confirm').value;
if (inputValueNumber == '' || inputValueName == '' || inputValueNumber.match(/[^A-Za-z0-9]+/) || inputValueConfirm !== inputValueNumber) {
loginButton.disabled = true;
} else {
loginButton.disabled = false;
}
},
login() {
const inputUser = this.user.name;
const inputNmber = this.user.number;
//storeに登録情報を送信する
this.$store.dispatch('setInfo', {
number: inputNmber,
name: inputUser,
});
usersCollection
.where('number', '==', inputNmber)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty) {
//新規ユーザーの場合
console.log('new-user');
usersCollection.add({
name: inputUser,
number: inputNmber,
startTime: firebase.firestore.FieldValue.serverTimestamp(), //初回ログイン時間
});
} else {
//既にユーザーが存在する場合
console.log('exist-user');
querySnapshot.forEach((doc) => {
usersCollection.doc(doc.id).set(
{
continueTime: firebase.firestore.FieldValue.serverTimestamp(), //途中参加時間
},
{ merge: true }
);
});
}
})
.catch(function (error) {
console.log('Error getting documents: ', error);
});
this.$router.push('/page01/');
},
},
};
</script>
import createPersistedState from 'vuex-persistedstate';
export default ({ store }) => {
window.onNuxtReady(() => {
createPersistedState({})(store);
});
};
export const state = () => ({
userInfo: {
number: '',
name: '',
},
});
export const mutations = {
setInfo(state, payload) {
state.userInfo.number = payload.number;
state.userInfo.name = payload.name;
},
};
export const actions = {
setInfo({ commit }, payload) {
commit('setInfo', {
number: payload.number,
name: payload.name,
});
},
};
export const getters = {
userInfo: (state) => {
return state.userInfo;
},
};
export default function ({ store, redirect }) {
// ユーザーが認証されていない場合
if (!store.state.userInfo.number) {
return redirect('/');
}else{
return redirect('/page01');
}
}
説明
page/index.vue
inputタグに情報が入力されているか、ユーザIDが正しいか等をチェック
入力情報がOKならログインボタンが押せるようになる
inputCheck() {
//ユーザIDと名前の空欄チャック
const loginButton = document.querySelector('.container__wrap--start');
const inputValueNumber = document.querySelector('.container__info--number').value;
const inputValueName = document.querySelector('.container__info--name').value;
const inputValueConfirm = document.querySelector('.container__info--confirm').value;
if (inputValueNumber == '' || inputValueName == '' || inputValueNumber.match(/[^A-Za-z0-9]+/) || inputValueConfirm !== inputValueNumber) {
loginButton.disabled = true;
} else {
loginButton.disabled = false;
}
},
page/index.vue
storeに入力情報を登録する
const inputUser = this.user.name;
const inputNmber = this.user.number;
//storeに登録情報を送信する
this.$store.dispatch('setInfo', {
number: inputNmber,
name: inputUser,
});
page/index.vue
inputNmberでDBの情報を検索し、情報がない場合は新規登録する
usersCollection
.where('number', '==', inputNmber)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty) {
//新規ユーザーの場合
console.log('new-user');
usersCollection.add({
name: inputUser,
number: inputNmber,
startTime: firebase.firestore.FieldValue.serverTimestamp(), //初回ログイン時間
});
}
page/index.vue
inputNmberでDBの情報を検索し、情報がある場合は途中参加時間のみ追加する
"{ merge: true }"とすることで存在する情報はそのままに、途中参加時間のみ追加できる
else {
//既にユーザーが存在する場合
console.log('exist-user');
querySnapshot.forEach((doc) => {
usersCollection.doc(doc.id).set(
{
continueTime: firebase.firestore.FieldValue.serverTimestamp(), //途中参加時間
},
{ merge: true }
);
});
}
page/index.vue
上記どちらかの工程が済んだ後、/page01に飛ぶ
this.$router.push('/page01/');
vuex-persistedstateの使用について
このままコピペでもOK
import createPersistedState from 'vuex-persistedstate';
export default ({ store }) => {
window.onNuxtReady(() => {
createPersistedState({})(store);
});
};
store/index.jsについて
ここにログイン情報(ユーザID:number、氏名:name)を保管する記載をしている
export const state = () => ({
userInfo: {
number: '',
name: '',
},
});
export const mutations = {
setInfo(state, payload) {
state.userInfo.number = payload.number;
state.userInfo.name = payload.name;
},
};
export const actions = {
setInfo({ commit }, payload) {
commit('setInfo', {
number: payload.number,
name: payload.name,
});
},
};
export const getters = {
userInfo: (state) => {
return state.userInfo;
},
};
middleware/authenticated.jsについて
store/index.jsの情報を元に、どこにリダイレクトするかを決めている
export default function ({ store, redirect }) {
// ユーザーが認証されていない場合
if (!store.state.userInfo.number) {
return redirect('/');
}else{
return redirect('/page01');
}
}
middlewareについて
middlewareについての読み込みはnuxt.config.jsに記載してもOK(全ページに適用する)、
各ページごとに設定したい場合はそれぞれに記載でもOK
//全ページに適用する場合
export default {
:
router: {
middleware: 'authenticated'
}
:
}
// 各ページごとに設定したい場合
<script>
import firebase from '@/plugins/firebase';
const db = firebase.firestore();
:
export default {
:
mounted() {
:
},
methods: {
:
},
middleware: 'authenticated',
};
</script>
以上となります。
最後に
初めてFirebaseを使用して作成したので、もっと良い記述方法あれば教えてください。
良ければ記事をLike、Twitterのフォローをお願いします。
Discussion