SPA mit Vue.js, vuetify, Laravel Lumen und Passport V

Teil 5: Integration der Authentifizierung im Frontend durch Nutzung des State Handlings mit Vuex

Albrecht Mauersberger
6 min readApr 10, 2019

Im letzten Teil unserer Tutorialseite wollen wir nun noch das fehlende Puzzlestück Authentifizierung im Frontend integrieren. Es soll ein Loginformular entstehen, das bei erfolgreichem Login in einen Adminbereich leitet, wo die bisher eingetragenen Kontaktanfragen hinterlegt sind. Die Logindaten werden dabei an unsere in Teil 2 erstellten API-Endpunkte gesendet, um einen access_token zu erhalten. Dieser wird dann genutzt, um Zugriff auf die Kontaktdaten zu erhalten. Der Zustand des Nutzers wird dabei in einem Vuex Store vorgehalten.

Im einzelnen werden also folgende Elemente benötigt:

  • ein Vuex-Store mit bestimmten Zuständen und Methoden zum Login/Logout und zum Ablegen der Token im LocalStorage
  • Eine Erweiterung von axios, um die vorhandenen Token mitzusenden
  • Eine Erweiterung des Routers, um Routen für das Login und den geschützten Adminbereich einzurichten
  • ein Login-Formular, in welches der Nutzer seine Zugangsdaten eingeben kann
  • der Adminbereich, in welchem die eingetragenen Kontaktanfragen angezeigt werden

Der Vuex Store

Als erstes erstellen wir uns einen Vuex Store. Dieser liegt parallel zur app.js im Ordner js und beinhaltet 4 wichtige Teile:

  • state : hier werden die Zustandsattribute definiert, welche in unserem Fall der eigentliche status , ein eventueller access_token und ein user-Objekt sind
  • mutations : durch Aufruf dieser Mutationen kann eine Änderung der Zustandsattribute angestoßen werden. Bei uns können Zustandsänderungn durch den Loginvorgang auth_request , den erfolgreichen Login auth_success , den Loginfehler auth_error und den Logout logout hervorgerufen werden
  • actions : das sind die eigentlichen Aktionen, die aufgerufen werden und folglich die Mutationen auslösen, in unserem Fall login und logout. Beim Login wird erst die Mutation auth_requestangestoßen, dann wird der eigentliche Request auf /login gemacht. Kommt ein valider Token zurück, wird dieser im LocalStorage hinterlegt und die Mutation auth_success angestoßen, bei einem Fehler wird auth_error getriggered und die Elemente aus dem LocalStorage entfernt. Beim Logout werden ebenfalls die Elemente aus dem LocalStorage entfernt, allerdings dann die Mutation logout angestoßen, die auch den access_token aus dem Store entfernt.
  • getters : sind quasi computed properties, die wir bereits von Vue konnen, für den Store. Am Ende sind es berechnete Zustände, in unserem Fall isLoggedIn und authStatus , welche wir in unseren Vue-Komponenten verwenden können

Zusammen sieht der Store nun wie folgt aus:

Wichtig zu erwähnen ist, dass neben Vuex auch hier Vue und Axios importiert werden. Außerdem übergeben wir bereits im Store die axios-Instanz an die Vue-Instanz, sodass der axios Import und das Binding aus der app.js entfernt werden kann. Darüber hinaus geben wir den Token, sofern dieser im LocalStorage vorhanden ist, dem axios-Request als Authorization-Header mit.

Vuex Store initialisieren

Um den Store dann der Anwendung bekannt zu machen, muss dieser in der app.js importiert und der Vue-Instanz übergeben werden. Wir sehen im folgenden Quellcode auch, dass axios, wie bereits beschrieben, nicht mehr erneut importiert wird. Wichtig ist, dass store der Vue-Initialisierung mitgegeben wird (Zeile 14).

Erweiterung des Routers

Der Router muss um 2 Elemente erweitert werden. Zum einen die neuen Routen für das Login und den Adminbereich, wobei bei diesem die Meta-Information hinterlegt wird, dass er eine Authentifizierung benötigt.

{
path: '/login',
name: 'login',
component: Login
},
{
path: '/admin',
name: 'admin',
component: Admin,
meta: {
requiresAuth: true
}
},

Außerdem muss der Router nun bei jedem Request prüfen, ob es sich um einen geschützten Zugriff handelt und wenn ja, entsprechend prüfen, ob der zugreifende Nutzer eingeloggt ist. Das bedeutet, dass der Vuex Store auch im Router importiert werden muss.

import store from './store'...router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (store.getters.isLoggedIn) {
next()
return
}
next('/login')
} else {
next()
}
})

Zusammengefasst sieht der Router am Ende wie folgt aus:

Das Login-Formular

Um in den geschützten Adminbereich zu kommen, wird ein Login-Formular benötigt. Die dort eingetragenen Nutzerdaten werden an die login-Funktion des Stores über geben, der entsprechend diese an die API sendet, um im Erfolgsfall die access_token im LocalStorage zu hinterlegen. Ist dieser Vorgang erfolgreich, wird der Nutzer in den Adminbereich weitergeleitet.

Für das Login-Formular nutzen wir wieder Vuetify-Komponenten, die wir allerdings sämtlich schon mit dem Kontaktformular aus Teil 4 behandelt haben und somit nicht neu sein sollten.

Wenn wir daraufhin das Login-Formular direkt im Browser via /login aufrufen, sollte folgende Ansicht entstehen:

Der Admin-Bereich

Der Admin-Bereich nutzt den beim Login erhaltenen access_token, um per POST an die API /private/contacts die bisher eingetragenen Anfragen aus dem Kontaktformular abzufragen. Diese werden in einer Datentabelle, die Vuetify mitbringt, ausgegeben. Gibt die API einen 401-Fehler zurück, d.h. wir konnten uns bspw. aufgrund eine abgelaufenen Tokens nicht authentifizieren, leiten wir zurück zum Login-Formular.

Hinweis: die API gibt nur eine 401-Fehler zurück, wenn der DEBUG_MODE der .env -Datei der Lumen-API auf false steht, ansonsten gibt es eine 500er-Antwort.

Damit sollte der Adminbereich, sofern bereits Kontaktanfragen vorliegen, folgendermaßen aussehen:

Ergänzung des Logins in der Navigation

Zum Abschluss wollen wir noch die Navigation erweitern, damit man sich über diese auch einloggen kann. Dafür ergänzen wir ein Login-Icon, wenn man noch nicht eingeloggt ist. Ist man angemeldet, soll ein Admin-Menü mit 2 Unterpunkten entstehen: Adminbereich (linkt zu /admin) und Logout, welches die dazugehörige Aktion des Vuex-Stores anstößt. Wir erweitern also die Navigation.vue auf folgenden Stand (wichtig sind Zeilen 48-77 und der script-Teil):

Damit sollte die Navigation wie folgt aussehen:

Der Quellcode

Zum Abschluss des 5-teiligen Tutorials kann sich der komplette Quellcode im Repository https://github.com/aibim/vue-lumen-tutorial angeschaut werden. Feedback und Verbesserungsvorschläge sind immer gern gesehen.

Fazit und Ausblick

Gratulation! Wir haben allerhand in 5 Teilen geschafft: wir haben ein komplettes Backend mit Authentifizierung implementiert, in welchem wir Datenbankmigrationen vorgenommen haben, um das Schema komfortabel iterativ aufzubauen und mit Eloquent ein ORM kennengelernt, um schnell Daten zu erhalten und miteinander verknüpfen zu können.

Außerdem haben wir ein komplettes Frontend erstellt, in welchem wir ein Routing vorgenommen und die API zur Datenausgabe angebunden haben. Wir haben Libraries wie Vuex für Login/Logout bzw. State Handling im allgemeinen und Vuetify zur Integration von Material Design Komponenten genutzt.

Zuguterletzt haben wir uns mit der lokalen Entwicklung mittels Webpack auseinandergesetzt, diverse Loader kennengelernt und sogar schon angefangen, die Anwendung zu optimieren, indem wie z.B. bei Vuetify A-la-Carte eingesetzt haben.

An dieser Stelle sei auch noch ein kleiner Ausblick gegeben: die nächsten Schritte könnten darin bestehen, die Entwicklungsumgebungen lokal/produktiv weiter zu finalisieren (sowohl im Backend als auch im Frontend), es könnte z.B. uglifyJS beim Bundling mittels Webpack eingesetzt werden, um geringere Dateigrößen zu erhalten und denkbar wäre auch, serverseitiges Rendering zu integrieren, um besseres SEO zu betreiben.

In dieser Tutorialserie soll es das aber gewesen sein, ich freue mich über Feedback und Diskussion!

Vorherige Teile dieser Serie:

--

--