Authentifizierung (Einloggen!) ist ein entscheidender Teil vieler Websites. Lassen Sie uns sehen, wie man dies auf einer Website mit Vue umsetzt, genauso wie es mit jedem benutzerdefinierten Backend erfolgen kann. Vue kann die Authentifizierung nicht allein durchführen – wir benötigen dafür einen anderen Dienst, wir werden also einen anderen Dienst (Firebase) dafür verwenden, und dann das gesamte Erlebnis in Vue integrieren.
Die Authentifizierung funktioniert bei Single Page Applications (SPAs) ganz anders als bei Websites, die jede Seite neu laden. Sie *müssen* keine SPA mit Vue erstellen, aber wir werden dies in diesem Tutorial tun.
Hier ist der Plan. Wir erstellen eine Benutzeroberfläche für Benutzer zum Einloggen, und die übermittelten Daten werden an einen Server gesendet, um zu prüfen, ob der Benutzer existiert. Wenn ja, erhalten wir ein Token. Das ist sehr nützlich, da es auf unserer gesamten Website verwendet wird, um zu prüfen, ob der Benutzer noch angemeldet ist. Wenn nein, kann sich der Benutzer immer registrieren. Mit anderen Worten, es kann in vielen bedingten Kontexten verwendet werden. Darüber hinaus, wenn wir Informationen vom Server benötigen, die eine Anmeldung erfordern, wird das Token über die URL an den Server gesendet, damit Informationen nur an angemeldete Benutzer gesendet werden.
Die vollständige Demo dieses Tutorials finden Sie auf GitHub für diejenigen, die den Code gerne durchlesen. Der Rest von uns kann dem Artikel folgen. Die Startdatei ist ebenfalls auf GitHub, damit Sie uns beim gemeinsamen Programmieren folgen können.
Nachdem Sie das Repository heruntergeladen haben, führen Sie npm install in Ihrem Terminal aus. Wenn Sie diese Anwendung komplett selbst erstellen möchten, müssen Sie Vuex, Vue Router und axios installieren. Wir werden für dieses Projekt auch Firebase verwenden, nehmen Sie sich also einen Moment Zeit, um ein kostenloses Konto einzurichten und dort ein neues Projekt zu erstellen.

Nachdem Sie das Projekt zu Firebase hinzugefügt haben, gehen Sie zum Authentifizierungsbereich und richten Sie eine Anmeldemethode ein, bei der wir den traditionellen E-Mail/Passwort-Anbieter verwenden werden, der auf unseren Firebase-Servern gespeichert wird.

Danach gehen wir zur Firebase Auth REST API-Dokumentation, um unsere Registrierungs- und Anmelde-API-Endpunkte zu erhalten. Wir benötigen einen API-Schlüssel, um diese Endpunkte in unserer App zu verwenden, und dieser ist in den Firebase-Projekteinstellungen zu finden.
Firebase bietet Authentifizierung über das SDK an, aber wir verwenden die Auth-API, um die Authentifizierung über beliebige benutzerdefinierte Backend-Server zu demonstrieren.
In unserer Startdatei haben wir das Registrierungsformular unten. Wir halten die Dinge hier ziemlich einfach, da wir uns auf das Erlernen der Konzepte konzentrieren.
<template>
<div id="signup">
<div class="signup-form">
<form @submit.prevent="onSubmit">
<div class="input">
<label for="email">Mail</label>
<input
type="email"
id="email"
v-model="email">
</div>
<div class="input">
<label for="name">Your Name</label>
<input
type="text"
id="name"
v-model.number="name">
</div>
<div class="input">
<label for="password">Password</label>
<input
type="password"
id="password"
v-model="password">
</div>
<div class="submit">
<button type="submit">Submit</button>
</div>
</form>
</div>
</div>
</template>
Wenn wir nicht mit einer SPA arbeiten würden, würden wir natürlich axios verwenden, um unsere Daten innerhalb des Skript-Tags wie folgt zu senden
axios.post('https://identitytoolkit.googleapis.com/v1/account
s:signUp?key=[API_KEY]', {
email: authData.email,
password: authData.password,
returnSecureToken: true
})
.then(res => {
console.log(res)
})
.catch(error => console.log(error))
}
}
Registrieren und Einloggen
Die Arbeit mit einer SPA (in diesem Fall Vue) unterscheidet sich stark vom oben genannten Ansatz. Stattdessen senden wir unsere Autorisierungsanfragen über Vuex in unseren Aktionen in der Datei store.js. Wir tun dies, weil wir möchten, dass die gesamte App über jede Änderung des Authentifizierungsstatus des Benutzers informiert ist.
actions: {
signup ({commit}, authData) {
axios.post('https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]', {
email: authData.email,
password: authData.password,
returnSecureToken: true
})
.then(res => {
console.log(res)
router.push("/dashboard")
})
.catch(error => console.log(error))
},
login ({commit}, authData) {
axios.post(https://identitytoolkit.googleapis.com/v1/accounts:signIn?key=[API_KEY]', {
email: authData.email,
password: authData.password,
returnSecureToken: true
})
.then(res => {
console.log(res)
router.push("/dashboard")
})
.catch(error => console.log(error))
}
}
Wir können im Grunde das Gleiche für die Anmeldemethode verwenden, aber stattdessen den Anmelde-API-Endpunkt nutzen. Dann übergeben wir sowohl die Registrierung als auch das Einloggen von den Komponenten an ihre jeweiligen Aktionen im Store.
methods : {
onSubmit () {
const formData = {
email : this.email,
name : this.name,
password : this.password
}
this.$store.dispatch('signup', formData)
}
}
}
formData enthält die Daten des Benutzers.
methods : {
onSubmit () {
const formData = {
email : this.email,
password : this.password
}
this.$store.dispatch('login', {email: formData.email, password: formData.password})
}
}
Wir nehmen die Authentifizierungsdaten (d. h. das Token und die Benutzer-ID), die vom Registrierungs-/Anmeldeformular empfangen wurden, und verwenden sie als Zustand mit Vuex. Anfangs wird dies null ergeben.
state: {
idToken: null,
userId: null,
user: null
}
Wir erstellen nun eine neue Methode namens authUser in den Mutationen, die die aus der Antwort gesammelten Daten speichert. Wir müssen den Router in den Store importieren, da wir ihn später benötigen werden.
import router from '/router'
mutations : {
authUser (state, userData) {
state.idToken = userData.token
state.userId = userData.userId
}
}
Innerhalb des .then-Blocks in den Registrierungs-/Anmeldemethoden in unseren Aktionen übergeben wir unsere Antwort an die gerade erstellte authUser-Mutation und speichern sie im lokalen Speicher.
actions: {
signup ({commit}, authData) {
axios.post('https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]'), {
email: authData.email,
password: authData.password,
returnSecureToken: true
})
.then(res => {
console.log(res)
commit('authUser', {
token: res.data.idToken,
userId: res.data.localId
})
localStorage.setItem('token', res.data.idToken)
localStorage.setItem('userId', res.data.localId)
router.push("/dashboard")
})
.catch(error => console.log(error))
},
login ({commit}, authData) {
axios.post('https://identitytoolkit.googleapis.com/v1/accounts:signIn?key=[API_KEY]'), {
email: authData.email,
password: authData.password,
returnSecureToken: true
})
.then(res => {
console.log(res)
commit('authUser', {
token: res.data.idToken,
userId: res.data.localId
})
localStorage.setItem('token', res.data.idToken)
localStorage.setItem('userId', res.data.localId)
router.push("/dashboard")
})
.catch(error => console.log(error))
}
}
Einrichtung eines Auth-Guards
Nachdem wir nun unser Token innerhalb der Anwendung gespeichert haben, werden wir dieses Token bei der Einrichtung unseres Auth-Guards verwenden. Was ist ein Auth-Guard? Er schützt das Dashboard vor unbefugten Benutzern, die ohne Token darauf zugreifen.
Zuerst gehen wir in unsere Routendatei und importieren den Store. Der Store wird wegen des Tokens importiert, das den Anmeldestatus des Benutzers bestimmt.
import store from './store.js'
Dann gehen wir innerhalb unseres Routen-Arrays zum Dashboard-Pfad und fügen die Methode beforeEnter hinzu, die drei Parameter erhält: to, from und next. Innerhalb dieser Methode sagen wir einfach, dass wenn die Tokens gespeichert sind (was automatisch geschieht, wenn authentifiziert), dann next aufgerufen wird, was bedeutet, dass die Navigation zur angegebenen Route fortgesetzt wird. Andernfalls leiten wir den nicht authentifizierten Benutzer zurück zur Registrierungsseite.
{
path: '/dashboard',
component: DashboardPage,
beforeEnter (to, from, next) {
if (store.state.idToken) {
next()
}
else {
next('/signin')
}
}
}
Erstellung des UI-Zustands
Zu diesem Zeitpunkt können wir das Dashboard immer noch in der Navigation sehen, egal ob wir angemeldet sind oder nicht, und das ist nicht das, was wir wollen. Wir müssen eine weitere Methode unter den Gettern namens ifAuthenticated hinzufügen, die prüft, ob das Token in unserem Zustand null ist, und dann die Navigationselemente entsprechend aktualisiert.
getters: {
user (state) {
return state.user
},
ifAuthenticated (state) {
return state.idToken !== null
}
}
Als Nächstes öffnen wir die Header-Komponente und erstellen eine Methode namens auth innerhalb der computed-Eigenschaft. Diese wird zu den ifAuthenticated-Gettern im Store aufgerufen, die wir gerade erstellt haben. ifAuthenticated gibt false zurück, wenn kein Token vorhanden ist, was automatisch bedeutet, dass auth ebenfalls null wäre und umgekehrt. Danach fügen wir einen v-if hinzu, um zu prüfen, ob auth null ist oder nicht, und bestimmen so, ob die Dashboard-Option in der Navigation angezeigt wird.
<template>
<header id="header">
<div class="logo">
<router-link to="/">Vue Authenticate</router-link>
</div>
<nav>
<ul>
<li v-if='auth'>
<router-link to="/dashboard">Dashboard</router-link>
</li>
<li v-if='!auth'>
<router-link to="/signup">Register</router-link>
</li>
<li v-if='!auth'>
<router-link to="/signin">Log In</router-link>
</li>
</ul>
</nav>
</header>
</template>
<script>
export default {
computed: {
auth () {
return this.$store.getters.ifAuthenticated
}
},
}
</script>
Ausloggen
Was wäre eine Anwendung ohne Ausloggen-Button? Lassen Sie uns eine neue Mutation namens clearAuth erstellen, die sowohl das Token als auch die userId auf null setzt.
mutations: {
authUser (state, userData) {
state.idToken = userData.token
state.userId = userData.userId
},
clearAuth (state) {
state.idToken = null
state.userId = null
}
}
Dann übergeben wir in unserer logout-Aktion an clearAuth, löschen den lokalen Speicher und fügen router.replace('/') hinzu, um den Benutzer nach dem Ausloggen korrekt weiterzuleiten.
Zurück zur Header-Komponente. Wir haben eine onLogout-Methode, die unsere logout-Aktion im Store aufruft. Dann fügen wir ein @click zum Button hinzu, der die onLogout-Methode aufruft, wie wir unten sehen können
<template>
<header id="header">
<div class="logo">
<router-link to="/">Vue Authenticate</router-link>
</div>
<nav>
<ul>
<li v-if='auth'>
<router-link to="/dashboard">Dashboard</router-link>
</li>
<li v-if='!auth'>
<router-link to="/signup">Register</router-link>
</li>
<li v-if='!auth'>
<router-link to="/signin">Log In</router-link>
</li>
<li v-if='auth'>
<ul @click="onLogout">Log Out</ul>
</li>
</ul>
</nav>
</header>
</template>
<script>
export default {
computed: {
auth () {
return this.$store.getters.ifAuthenticated
}
},
methods: {
onLogout() {
this.$store.dispatch('logout')
}
}
}
</script>
Automatisches Einloggen? Klar!
Wir sind fast fertig mit unserer App. Wir können uns anmelden, einloggen und ausloggen, mit all den UI-Änderungen, die wir gerade vorgenommen haben. Aber wenn wir unsere App aktualisieren, verlieren wir die Daten und werden ausgeloggt, was bedeutet, dass wir von vorne beginnen müssen, da wir unser Token und unsere ID in Vuex gespeichert haben, was JavaScript ist. Das bedeutet, dass alles in der App im Browser neu geladen wird, wenn aktualisiert wird.
Was wir tun werden, ist, das Token aus unserem lokalen Speicher abzurufen. Indem wir das tun, können wir das Token des Benutzers im Browser haben, unabhängig davon, wann wir das Fenster aktualisieren, und den Benutzer sogar automatisch einloggen, solange das Token noch gültig ist.
Erstellen Sie eine neue Aktionsmethode namens AutoLogin, in der wir das Token und userId aus dem lokalen Speicher abrufen, nur wenn der Benutzer eines hat. Dann übergeben wir unsere Daten an die authUser-Methode in den Mutationen.
actions : {
AutoLogin ({commit}) {
const token = localStorage.getItem('token')
if (!token) {
return
}
const userId = localStorage.getItem('userId')
const token = localStorage.getItem('token')
commit('authUser', {
idToken: token,
userId: userId
})
}
}
Dann gehen wir zu unserer App.vue und erstellen eine created-Methode, in der wir autoLogin aus unserem Store aufrufen, wenn die App geladen wird.
created () {
this.$store.dispatch('AutoLogin')
}
Juhu! Damit haben wir die Authentifizierung erfolgreich in unserer App implementiert und können nun mit npm run build bereitstellen. Schauen Sie sich die Live-Demo an, um sie in Aktion zu sehen.
Die Beispiel-Website dient ausschließlich Demonstrationszwecken. Bitte teilen Sie keine echten Daten, wie Ihre echten E-Mail-Adressen und Passwörter, während Sie die Demo-App testen.
Das Speichern des Tokens im lokalen Speicher gilt nicht als Best Practice. Daten im lokalen Speicher sind anfällig für XSS-Angriffe.
Was wäre eine sichere Alternative? Frage aus Unwissenheit hier.
Sagt wer? Die einzige Alternative ist, es in den Cookies zu speichern, aber auch das hat seine Schwachstellen. Nichts falsch am lokalen Speicher. Vue ist von Natur aus sicher gegen XSS, es sei denn, Sie geben Benutzerinputs dummerweise als HTML aus.
Auch beim nächsten Mal posten Sie eine alternative Lösung. [redacted]
Das Wichtigste ist, XSS-Angriffe vollständig zu verhindern, denn wenn ein Angriff stattfindet, kann die Sitzung des Benutzers kompromittiert werden, egal ob Sie Cookies oder lokalen Speicher verwenden. Weitere Informationen finden Sie hier https://forum.vuejs.org/t/vuejs-jwt/14976/19
Außerdem ist Vue bereits sicher gegen XSS.
Bitte speichern Sie keine Passwörter oder Zugriffstoken im lokalen Speicher. Sie können von jedem JavaScript gelesen werden, das in jedem Browser-Tab eines Benutzers ausgeführt wird.
Ich verwende secure-ls, um den lokalen Speicher zu verschlüsseln.
Sie verwenden keine Signatur, HTTPS ist leicht zu umgehen und zu fälschen. Sowohl die Verwendung von HTTPS als auch die Signatur mit einem SHA512-Schlüssel wie in der Bankenkommunikation ist eine weitaus bessere Praxis, um die Datenherkunft sicherzustellen.
Können wir ein GitHub-Projekt für diesen schönen Code haben?
Vielleicht habe ich es auf den ersten Blick nicht gesehen https://github.com/Atanda1/vue-auth
Hey,
Danke für dieses Tutorial, es war sehr hilfreich.
Damit dieser Code funktioniert, musste ich AutoLogin in den Router verschieben. Der App.vue create() wird nach dem Router ausgeführt, was dazu führt, dass die Seite immer zur Anmeldeseite weitergeleitet wird.
Ich bin mir nicht sicher, ob dies ein Ergebnis meiner Konfiguration war, aber es schienen andere dieses Problem zu haben.
https://stackoverflow.com/questions/45290003/router-beforeeach-guard-executed-before-state-loaded-in-vue-created
Nochmals vielen Dank.
Interessant… das hatte ich noch nie erlebt, da es in App.vue funktioniert, aber ich schaue es mir an.
Hallo Leute, es gibt einen kleinen Fehler in der AuthLogin-Aktion
hat mich ewig aufgehalten, konnte ihn nur durch den Vergleich mit Davids funktionierender Version hier finden: https://github.com/Atanda1/vue-auth/blob/master/src/store.js
Guter Blog! Eine Frage hätte ich noch. Ist es sicher, den API-Schlüssel in store.js offenzulegen? Würde dieser Schlüssel nicht Administratorzugriff auf Ihr gesamtes Firebase-Projekt gewähren?
Es war ein Fehler und ich habe es erst nach der Veröffentlichung des Artikels erfahren, aber es gibt buchstäblich nichts in meiner Firebase-Datenbank und die leere Datenbank hat Regeln. Außerdem ist hier alles nur zu Demonstrationszwecken.
Hey, ich kann mich nicht einloggen, ich habe Firebase eingerichtet, aber es wurde ein Cross-Origin-Fehler beim Einloggen angezeigt... Wie stelle ich den Ursprung in Firebase ein?
Danke für das Teilen, das ist ein tolles Tutorial. Ein paar Fragen: 1) Wenn ein Benutzer falsche Anmeldedaten angibt, wie verhindere ich den 400-Fehler in der Konsole? und 2) Gibt es eine Möglichkeit, dass ein Benutzer, wenn er authentifiziert ist, dynamisch zu der angeforderten Route weitergeleitet wird, im Gegensatz zum hartcodierten Dashboard im Tutorial? Danke
1) Wenn Sie den Fehler nicht im
catch-Blockconsole.loggen, sollte es keinen Fehler in der Konsole geben.2) Es ist möglich, Sie müssen nur die
beforeEachder jeweiligen geschützten Route verwenden. Zum Beispiel, für die Dashboard-Route, anstatt den Benutzer einfach direkt zur signIn-Seite umzuleiten, wenn kein Token vorhanden ist, mitnext('/signin')Tun wir stattdessen dies
const signinpath = window.location.pathname;
next({ path: ‘/signin’, query: { redirect: signinpath } });
window.location.pathnameerfasst die beabsichtigte Route und übergibt sie als Query-Parameter namensredirect.Dann gehen wir zur
signIn-Aktion. Nachdem wir den Benutzer angemeldet haben, werden wir das Dashboard nicht hartcodieren, wie wir es im Artikel getan haben.router.push("/dashboard")Stattdessen fügen wir hinzu
router.push(this.$route.query.redirect)Dies holt den Query-Parameter (
redirect), den wir zuvor übergeben haben, und leitet den Benutzer dorthin.Finden Sie weitere Informationen hier: https://stackoverflow.com/a/51034158
Danke David. Zu Punkt 1: Ich habe den Code von GitHub heruntergeladen, npm in diesem Verzeichnis installiert und das Formular dort ausprobiert. Ich erhielt denselben Fehler in der Konsole, als ich absichtlich falsche Logins eingegeben habe.
„Fehler beim Laden der Ressource: Der Server hat mit dem Status 400 () geantwortet.“
Bei diesem Codeausschnitt scheint es: o.apply(e,arguments)}))),”function”==typeof fetch&&(fetch=e(fetch,(r=fetch,function(t,e)
Das wäre sinnvoll, da die Logins falsch sind, aber ich frage mich, ob es eine Möglichkeit gibt, sodass er ohne Fehlermeldung antwortet. Vielleicht liegt das nur daran, dass ich es von meiner lokalen Entwicklungsumgebung aus ausführe?
Entschuldigen Sie die möglicherweise dummen Fragen. Ich bin neu hier und schätze die Hilfe ;-)