Autentisering är en av de viktigaste delarna av alla webbapplikationer. I den här handledningen diskuteras tokenbaserade autentiseringssystem och hur de skiljer sig från traditionella inloggningssystem. I slutet av denna handledning ser du en helt fungerande demo skrivet i AngularJS och NodeJS.
Du kan också hitta ett brett urval av färdiga autentiseringsskript och appar på Envato Market, till exempel:
Eller om du kämpar med en bugg i din AngularJS-kod kan du skicka den till araneux på Envato Studio för att få den fixad.
Innan vi fortsätter med ett tokenbaserat autentiseringssystem, låt oss först titta på ett traditionellt autentiseringssystem.
Allt är bra fram till denna punkt. Webapplikationen fungerar bra, och den kan verifiera användare så att de får tillgång till begränsade slutpunkter. Men vad händer när du vill utveckla en annan klient, säger för Android, för din ansökan? Kommer du kunna använda den aktuella applikationen för att autentisera mobila klienter och att servera begränsat innehåll? Som det står för närvarande, nej. Det finns två huvudorsaker för detta:
I det här fallet behöver du en klientoberoende applikation.
I tokenbaserad autentisering används inte cookies och sessioner. En token kommer att användas för att autentisera en användare för varje förfrågan till servern. Låt oss omskapa det första scenariot med tokenbaserad autentisering.
Det kommer att använda följande kontrollflöde:
I det här fallet har vi ingen återkommande session eller cookie, och vi har inte returnerat något HTML-innehåll. Det betyder att vi kan använda denna arkitektur för vilken klient som helst för en specifik applikation. Du kan se arkitekturschemat nedan:
Så, vad är det här JWT?
JWT står för JSON webbtoken och är ett tokenformat som används i behörighetsrubriker. Denna token hjälper dig att utforma kommunikation mellan två system på ett säkert sätt. Låt oss omformulera JWT som "bärarens token" för syftet med denna handledning. En bärare token består av tre delar: rubrik, nyttolast och signatur.
Du kan se JWT-schemat och ett exempel på token nedan;
Du behöver inte implementera bärarens token generator eftersom du kan hitta versioner som redan finns på flera språk. Du kan se några av dem nedan:
Språk | Bibliotekets webbadress |
---|---|
NodeJS | http://github.com/auth0/node-jsonwebtoken |
PHP | http://github.com/firebase/php-jwt |
java | http://github.com/auth0/java-jwt |
Rubin | http://github.com/progrium/ruby-jwt |
.NETTO | http://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet |
Pytonorm | http://github.com/progrium/pyjwt/ |
Efter att ha täckt en del grundläggande uppgifter om tokenbaserad autentisering kan vi nu gå vidare med ett praktiskt exempel. Ta en titt på följande schema, varefter vi analyserar det mer detaljerat:
https://api.yourexampleapp.com
. Om många människor använder programmet kan flera servrar vara skyldiga att betjäna den begärda operationen.https://api.yourexampleapp.com
, Först kommer lastbalansen att hantera en förfrågan, och sedan kommer den att omdirigera klienten till en viss server.https://api.yourexampleapp.com
, back-end-applikationen kommer att fånga upp begäranhuvudet och extrahera tokeninformation från behörighetsrubriken. En databasfråga kommer att göras med hjälp av denna token. Om denna token är giltig och har det nödvändiga tillståndet att komma åt den begärda slutpunkten, fortsätter den. Om inte, kommer det att returnera en 403 svarskod (vilket indikerar en förbjuden status).Tokenbaserad autentisering kommer med flera fördelar som löser allvarliga problem. Några av dem är följande:
temp
mapp, åtminstone för första gången. Låt oss säga att du har flera servrar och en session skapas på den första servern. När du gör en annan förfrågan och din förfrågan faller på en annan server, kommer sessionsinformation inte att finnas och kommer att få ett "obehörigt" svar. Jag vet att du kan lösa det med en klibbig session. I tokenbaserad autentisering löses dock detta fall naturligt. Det finns inget klibbigt sessionsproblem, eftersom begäran-token avlyssnas vid varje förfrågan på vilken server som helst.Det är de vanligaste fördelarna med tokenbaserad autentisering och kommunikation. Det är slutet på det teoretiska och arkitektoniska samtalet om tokenbaserad autentisering. Tid för ett praktiskt exempel.
Du kommer att se två program för att visa tokenbaserad autentisering:
I back-end-projektet kommer service implementeringar, och serviceresultat kommer att finnas i JSON-format. Det finns ingen synvinkel som returneras i tjänster. I front-end-projektet kommer det att finnas ett AngularJS-projekt för front-end-HTML och sedan kommer den avancerade appen att fyllas av AngularJS-tjänster för att göra förfrågningar till backend-tjänsterna.
I back-end-projektet finns tre huvudfiler:
package.json
är för beredskapsförvaltning.modeller \ User.js
innehåller en användarmodell som ska användas för att göra databasoperationer om användare.server.js
är för projektstartning och förfråganhantering.Det är allt! Detta projekt är väldigt enkelt, så att du enkelt kan förstå huvudkonceptet utan att göra djupdykning.
"name": "angular-restful-auth", "version": "0.0.1", "beroenden": "express": "4.x", "body-parser": "~ 1.0.0" , "morgan": "senaste", "mongoose": "3.8.8", "jsonwebtoken": "0.4.0", "motorer": "nod": "> = 0.10.0"
package.json
innehåller beroenden för projektet: uttrycka
för MVC, kropp-parser
för att simulera efterbehandlingshantering i NodeJS, morgan
för begäran loggning, mungo
för vår ORM-ram för att ansluta till MongoDB, och jsonwebtoken
för att skapa JWT-tokens genom att använda vår användarmodell. Det finns också ett attribut som heter motorer
som säger att detta projekt är gjort med NodeJS versionen = = 0.10.0. Detta är användbart för PaaS-tjänster som Heroku. Vi kommer också att täcka det ämnet i en annan sektion.
var mongoose = kräver ('mongoose'); var Schema = mongoose.Scema; var UserSchema = nytt schema (email: String, lösenord: String, token: String); module.exports = mongoose.model ('User', UserSchema);
Vi sa att vi skulle generera en token genom att använda användarmodellens nyttolast. Denna modell hjälper oss att göra användaroperationer på MongoDB. I User.js
, användar-schemat är definierat och användarmodellen skapas genom att använda en mongoosmodell. Denna modell är redo för databasoperationer.
Våra beroenden definieras, och vår användarmodell är definierad, så nu låt oss kombinera alla dem som konstruerar en tjänst för hantering av specifika önskemål.
// Obligatoriska moduler var express = kräver ("express"); var morgan = kräver ("morgan"); var bodyParser = kräver ("body-parser"); var jwt = kräver ("jsonwebtoken"); var mongoose = kräver ("mongoose"); var app = express ();
I NodeJS kan du inkludera en modul i ditt projekt med hjälp av fordra
. Först måste vi importera de nödvändiga modulerna till projektet:
var port = process.env.PORT || 3001; var User = kräver ('./ models / User'); // Anslut till DB mongoose.connect (process.env.MONGO_URL);
Vår service tjänar genom en viss port. Om någon portvariabel definieras i systemmiljövariablerna kan du använda det eller vi har definierad port 3001
. Därefter ingår användarmodellen och databasanslutningen är etablerad för att göra vissa användaroperationer. Glöm inte att definiera en miljövariabel-MONGO_URL
-för databasanslutningsadressen.
app.use (bodyParser.urlencoded (extended: true)); app.use (bodyParser.json ()); app.use (Morgan ( "dev")); app.use (funktion (req, res, nästa) res.setHeader ('Access-Control-Allow-Origin', '*'); res.setHeader ('Access-Control-Allow-Methods', 'GET, POST '); res.setHeader (' Access-Control-Allow-Headers ',' X-Requested-With, Content Type, Authorization '); Nästa (););
I ovanstående avsnitt har vi gjort några konfigurationer för att simulera en HTTP-begäranhantering i NodeJS genom att använda Express. Vi tillåter förfrågningar att komma från olika domäner för att utveckla ett klientoberoende system. Om du inte tillåter det här kommer du att utlösa ett fel i CORS (Cross Origin Request Sharing Sharing) i webbläsaren.
Åtkomst-Control-allow-Origin
tillåtet för alla domäner.POSTA
och SKAFFA SIG
förfrågningar till denna tjänst.X-Begärd-Med
och innehållstyp
rubriker är tillåtna.app.post ('/ authenticate'), funktion (req, res) User.findOne (email: req.body.email, lösenord: req.body.password, funktion (err, användare) om (err) res.json (typ: false, data: "Fel uppstod:" + err); else if (user) res.json (typ: true, data: user, token: user.token); else res.json (typ: false, data: "Felaktigt email / password");;;);
Vi har importerat alla nödvändiga moduler och definierat vår konfiguration, så nu är det dags att definiera begäran hanterare. I ovanstående kod, när du gör en POSTA
begära att / autentisera
med användarnamn och lösenord får du en JWT
tecken. Först behandlas databasfrågan med användarnamn och lösenord. Om en användare finns, returneras användardata med dess token. Men, om det inte finns någon sådan användare som matchar användarnamnet och / eller lösenordet?
app.post ('/ signin', funktion (req, res) User.findOne (email: req.body.email, password: req.body.password, funktion (err, användare) om (err) res.json (typ: false, data: "Fel uppstod:" + err); annat om (användare) res.json (typ: false, data: "Användaren finns redan!"); annars var userModel = ny användare (); userModel.email = req.body.email; userModel.password = req.body.password; userModel.save (funktion (err, användare) user.token = jwt.sign (användare , process.env.JWT_SECRET); user.save (funktion (err, user1) res.json (typ: true, data: user1, token: user1.token););); );
När du gör en POSTA
begära att /logga in
med användarnamn och lösenord kommer en ny användare att skapas genom att använda upplagd användarinformation. På 19:e
linje kan du se att en ny JSON-token genereras genom att använda jsonwebtoken
modul, som har tilldelats till JWT
variabel. Autentiseringsdelen är OK. Vad händer om vi försöker komma åt en begränsad slutpunkt? Hur kan vi få tillgång till den slutpunkten?
app.get ('/ me', secureAuthorized, funktion (req, res) User.findOne (token: req.token, funktion (err, användare) om (err) res.json , data: "Fel uppstod:" + err); else res.json (typ: true, data: user);););
När du gör en SKAFFA SIG
begära att /mig
, du kommer att få aktuell användarinformation, men för att fortsätta med den önskade slutpunkten, ensureAuthorized
funktionen kommer att utföras.
funktion säkerställaAuthorized (req, res, next) var bärareToken; var bärareHeader = req.headers ["authorization"]; om (typ av bärareHeader! == 'odefinierad') var bärare = bärareHeader.split (""); bärareToken = bärare [1]; req.token = bearerToken; Nästa(); annat res.send (403);
I denna funktion avlyssnas begäranhuvuden och tillstånd
rubriken extraheras. Om en bärare token existerar i den här rubriken, tilldelas denna token req.token
för att kunna användas under hela förfrågan, och begäran kan fortsättas med användning av Nästa()
. Om ett token inte existerar får du ett 403 (förbjudet) svar. Låt oss gå tillbaka till handlaren /mig
, och använda req.token
för att hämta användardata med denna token. När du skapar en ny användare genereras en token och sparas i användarmodellen i DB. Dessa tokens är unika.
Vi har bara tre hanterare för det här enkla projektet. Därefter kommer du att se;
process.on ('uncaughtException', funktion (err) console.log (err););
NodeJS-appen kan krascha om ett fel uppstår. Med ovanstående kod förhindras den kraschen och en fellogg skrivs ut i konsolen. Och äntligen kan vi starta servern genom att använda följande kodbit.
// Starta servern app.listen (port, funktion () console.log ("Express-servern lyssnar på port" + port););
För att sammanfatta:
Vi är färdiga med back-end service. Så att den kan användas av flera kunder kan du distribuera det här enkla serverns program till dina servrar, eller kanske du kan distribuera i Heroku. Det finns en fil som heter Procfile
i projektets rotmapp. Låt oss använda vår service i Heroku.
Du kan klona back-end-projektet från det här GitHub-arkivet.
Jag kommer inte att diskutera hur man skapar en app i Heroku; Du kan hänvisa till den här artikeln för att skapa en Heroku-app om du inte har gjort det tidigare. När du har skapat din Heroku-app kan du lägga till en destination för ditt aktuella projekt genom att använda följande kommando:
git fjärrkontroll lägg till heroku
Nu har du klonat ett projekt och lagt till en destination. Efter git lägg till
och git commit
, Du kan driva din kod till Heroku genom att utföra git push heroku master
. När du framgångsrikt driver ett projekt, kommer Heroku att utföra npm installera
kommandot att ladda ner beroenden i temp
mapp på Heroku. Därefter startar programmet din och du kan komma åt din tjänst genom att använda HTTP-protokollet.
I front-end-projektet ser du ett AngularJS-projekt. Här kommer jag bara att nämna huvuddelarna i front-end-projektet, eftersom AngularJS inte är något som kan omfattas av en enda handledning.
Du kan klona projektet från det här GitHub-arkivet. I det här projektet ser du följande mappstruktur:
ngStorage.js
är ett bibliotek för AngularJS för att manipulera lokala lagringsoperationer. Det finns också en huvudlayout index.html
och partials som utökar huvudlayouten under partials
mapp. controllers.js
är för att definiera våra kontrolleråtgärder i fronten. services.js
är för att göra serviceanmälningar till vår tjänst som jag nämnde i det föregående projektet. Vi har en bootstrap-liknande fil som heter app.js
och i denna fil används konfigurationer och modulimport. Till sist, client.js
är för att servera statiska HTML-filer (eller bara index.html
, I detta fall); Det hjälper oss att servera statiska HTML-filer när du distribuerar till en server utan att använda Apache eller någon annan webbservrar.
...
I HTML-filen för huvudlayout ingår alla nödvändiga JavaScript-filer för AngularJS-relaterade bibliotek, samt vår anpassade kontroller, service och appfil.
använd strikt / * Controllers * / angular.module ('angularRestfulAuth') .controller ('HomeCtrl', ['$ rootScope', '$ scope', '$ location', '$ localStorage', 'Main'-funktionen ($ rootScope, $ scope, $ location, $ localStorage, Main) $ scope.signin = function () var formData = email: $ scope.email, lösenord: $ scope.password Main.signin (formData, funktion om (res.type == false) alert (res.data) annars $ localStorage.token = res.data.token; window.location = "/";, funktionen () $ rootScope.error = 'Misslyckades med att logga in';); $ scope.signup = function () var formData = email: $ scope.email, lösenord: $ scope.password Main.save (formData, funktion res.type == false) alert (res.data) annat $ localStorage.token = res.data.token; window.location = "/", funktionen () $ rootScope.error = 'Misslyckades med att signup ';); $ scope.me = function () Main.me (funktion (res) $ scope.myDetails = res;, funktion () $ rootScope.error =' Misslyckades med att hämta detaljer '; ); $ scope.logout = function () Main.logout (fu nction () window.location = "/", funktion () alert ("Misslyckades med att logga ut!"); ); ; $ scope.token = $ localStorage.token; ])
I ovanstående kod, den HomeCtrl
styrenheten definieras och vissa moduler injiceras som $ rootScope
och $ omfattning
. Dependensinjektion är en av de starkaste egenskaperna hos AngularJS. $ omfattning
är brovariabeln mellan kontroller och visningar i AngularJS som betyder att du kan använda testa
i sikte om du definierade det i en angiven kontroller som $ Scope.test = ...
I denna kontroller definieras vissa verktygsfunktioner, såsom:
logga in
för att skapa en inloggningsknapp på inloggningsformuläretBli Medlem
för anmälningsformulärshanteringmig
för att ange Me-knappen i layoutenI huvudlayouten, i huvudmenyn kan du se uppgifter-ng-regulator
attribut med ett värde HomeCtrl
. Det betyder att den här menyn dom
element kan dela med HomeCtrl
. När du klickar på anmälningsknappen i formuläret kommer registreringsfunktionen i kontrollerfilen att utföras, och i denna funktion används sign up-tjänsten från Huvudsaklig
tjänst som redan injiceras i denna kontroller.
Huvudstrukturen är visa -> controller -> service
. Den här tjänsten gör enkla Ajax-förfrågningar till back-end för att få specifika uppgifter.
använd strikt angular.module ('angularRestfulAuth') .factory ('Main', ['$ http', '$ localStorage', funktion ($ http, $ localStorage) var baseUrl = "your_service_url"; funktionsbyteUser förlänga (nuvarande användare, användare); funktion urlBase64Decode (str) var output = str.replace ('-', '+'). ersätt ('_', '/'); byta (output.length% 4) fall 0: break; case 2: output + = '=='; break; case 3: output + = '='; break; default: kasta 'Illegal base64url string!'; returnera window.atob (output); funktion getUserFromToken () var token = $ localStorage.token; var användare = ; om (typof token! == 'undefined') var kodad = token.split ('.') [1]; user = JSON. parse (urlBase64Decode (kodad)); returnera användare; var currentUser = getUserFromToken (); returnera spara: funktion (data, framgång, fel) $ http.post (baseUrl + '/ signin', data) .success framgång) .error (fel), signin: funktion (data, framgång, fel) $ http.post (baseUrl + '/ authenticate', data) .success (framgång) .ror (fel), mig: funktion framgång, fel) $ htt p.get (baseUrl + '/me').success(success).error(error), logout: funktion (framgång) changeUser (); ta bort $ localStorage.token; Framgång(); ; ]);
I ovanstående kod kan du se servicefunktioner som att göra förfrågningar om autentisering. I controller.js har du kanske redan insett att det finns funktioner som Main.me
. Detta Huvudsaklig
tjänsten har injicerats i regulatorn, och i kontrollenheten heter tjänsterna som hör till denna tjänst direkt.
Dessa funktioner är helt enkelt Ajax-förfrågningar till vår tjänst som vi distribuerade tillsammans. Glöm inte att ange tjänstadressen baseUrl
i ovanstående kod. När du distribuerar din tjänst till Heroku får du en tjänstadress som appname.herokuapp.com
. I ovanstående kod kommer du att ställa in var baseUrl = "appname.herokuapp.com"
.
I registrerings- eller inloggningsdelen av ansökan svarar bärarens token på begäran och denna token sparas i lokal lagring. När du gör en förfrågan till en tjänst i back-enden måste du sätta denna token i rubrikerna. Du kan göra detta med hjälp av AngularJS-interceptorer.
$ httpProvider.interceptors.push ($ '$ q', '$ location', '$ localStorage', funktionen ($ q, $ location, $ localStorage) return 'request': function (config) config.headers = config.headers || ; om ($ localStorage.token) config.headers.Authorization = 'Bearer' + $ localStorage.token; returnera config;, 'responseError': funktion (svar) if status === 401 || response.status === 403) $ location.path ('/ signin'); returnera $ q.reject (svar);;]);
I ovanstående kod avlyssnas varje förfrågan och en behörighetsrubrik och värde läggs i rubrikerna.
I front-end-projektet har vi några sidor som logga in
, Bli Medlem
, profiluppgifter
, och vb
. Dessa partiella sidor är relaterade till specifika kontroller. Du kan se den relationen i app.js
:
angular.module ('angularRestfulAuth', ['ngStorage', 'ngRoute']) .config (['$ routeProvider', '$ httpProvider', funktion ($ routeProvider, $ httpProvider) $ routeProvider. templateUrl: 'partials / home.html', controller: 'HomeCtrl') när ('/ signin', templateUrl: 'partials / signin.html', controller: 'HomeCtrl'). ', templateUrl:' partials / signup.html ', controller:' HomeCtrl '). När (' / me ', templateUrl:' partials / me.html ', controller:' HomeCtrl ') omdirigeraTo: '/');
Som du lätt kan förstå i ovanstående kod, när du går till /
, de home.html
sidan kommer att göras. Ett annat exempel: om du går till /Bli Medlem
, signup.html
kommer att göras. Denna återgivningsoperation kommer att göras i webbläsaren, inte på serverns sida.
Du kan se hur allt vi diskuterade i denna handledning fungerar i praktiken genom att kolla in den här fungerande demo.
Tokenbaserade autentiseringssystem hjälper dig att konstruera ett autentiserings- / auktoriseringssystem medan du utvecklar klientoberoende tjänster. Genom att använda denna teknik fokuserar du bara på dina tjänster (eller API).
Autentiseringsdelen kommer att hanteras av det tokenbaserade autentiseringssystemet som ett lager framför dina tjänster. Du kan komma åt och använda tjänster från alla klienter som webbläsare, Android, iOS eller en stationär klient.
Och om du letar efter färdiga lösningar, kolla in autentiseringsskript och appar på Envato Market.