Autentisering er en av de viktigste delene av et hvilket som helst webprogram. I denne opplæringen diskuterer vi tokenbaserte autentiseringssystemer og hvordan de adskiller seg fra tradisjonelle påloggingssystemer. På slutten av denne opplæringen vil du se en fullt fungerende demo skrevet i AngularJS og NodeJS.
Du kan også finne et bredt utvalg av ferdige autentiseringsskript og -programmer på Envato Market, for eksempel:
Eller, hvis du sliter med en feil i AngularJS-koden din, kan du sende den til araneux på Envato Studio for å få den løst.
Før vi fortsetter med et tokenbasert autentiseringssystem, må vi først se på et tradisjonelt autentiseringssystem.
Alt er greit til dette punktet. Nettapplikasjonen fungerer bra, og den er i stand til å autentisere brukere slik at de får tilgang til begrensede endpoints; Men hva skjer når du vil utvikle en annen klient, si for Android, for din søknad? Vil du kunne bruke det nåværende programmet til å autentisere mobilklienter og å betjene begrenset innhold? Som det står for tiden, nei. Det er to hovedårsaker til dette:
I dette tilfellet trenger du et klientuafhængig program.
I tokenbasert autentisering vil ikke informasjonskapsler og økter bli brukt. Et token vil bli brukt til å godkjenne en bruker for hver forespørsel til serveren. La oss omforme det første scenariet med tokenbasert godkjenning.
Den vil bruke følgende kontrollflow:
I dette tilfellet har vi ikke returnert økt eller informasjonskapsel, og vi har ikke returnert noe HTML-innhold. Det betyr at vi kan bruke denne arkitekturen til enhver klient for et bestemt program. Du kan se arkitekturskjemaet nedenfor:
Så, hva er dette JWT?
JWT står for JSON Web Token og er et token-format som brukes i autorisasjonsoverskrifter. Dette token hjelper deg å designe kommunikasjon mellom to systemer på en sikker måte. La oss omformulere JWT som "bærer token" i forbindelse med denne opplæringen. En bærer token består av tre deler: header, nyttelast og signatur.
Du kan se JWT-skjemaet og et eksempeltegn under
Du trenger ikke å implementere bærer token generator fordi du kan finne versjoner som allerede finnes på flere språk. Du kan se noen av dem nedenfor:
Språk | Bibliotekets URL |
---|---|
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 |
.NETT | http://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet |
Python | http://github.com/progrium/pyjwt/ |
Etter å ha dekket noen grunnleggende opplysninger om tokenbasert autentisering, kan vi nå gå videre med et praktisk eksempel. Ta en titt på følgende skjema, hvoretter vi analyserer det mer detaljert:
https://api.yourexampleapp.com
. Hvis mange bruker programmet, kan det hende at flere servere må betjene den forespurte operasjonen.https://api.yourexampleapp.com
, Først vil belastningsbalansen håndtere en forespørsel, og da vil den omdirigere klienten til en bestemt server.https://api.yourexampleapp.com
, back-end-applikasjonen vil fange opp forespørselsoverskriften og trekke ut tokeninformasjon fra autorisasjonsoverskriften. En database spørring vil bli gjort ved å bruke denne token. Hvis dette token er gyldig og har den nødvendige tillatelsen til å få tilgang til det forespurte sluttpunktet, fortsetter det. Hvis ikke, vil det returnere en 403 svarkode (som indikerer en forbudt status).Tokenbasert autentisering kommer med flere fordeler som løser alvorlige problemer. Noen av dem er som følger:
temp
mappe, i hvert fall for første gang. La oss si at du har flere servere og en økt er opprettet på den første serveren. Når du gjør en annen forespørsel, og forespørselen din faller i en annen server, vil øktinformasjon ikke eksistere og vil få et "uautorisert" svar. Jeg vet, du kan løse det med en klebrig økt. Men i token-basert godkjenning, er dette tilfellet løst naturlig. Det er ikke et klistret øktproblem, fordi forespørselstoken blir avlyst på hver forespørsel på hvilken som helst server.Det er de vanligste fordelene med tokenbasert autentisering og kommunikasjon. Det er slutten på teoretisk og arkitektonisk snakk om tokenbasert autentisering. Tid for et praktisk eksempel.
Du vil se to applikasjoner for å demonstrere tokenbasert autentisering:
I back-end-prosjektet vil det bli serviceimplementeringer, og serviceresultater vil være i JSON-format. Det er ingen visning returnert i tjenester. I front-end-prosjektet vil det være et AngularJS-prosjekt for front-end HTML, og deretter vil front-end-appen bli befolket av AngularJS-tjenester for å gjøre forespørsler til back-end-tjenestene.
I back-end-prosjektet er det tre hovedfiler:
package.json
er for avhengighetsstyring.modeller \ User.js
inneholder en brukermodell som vil bli brukt til å gjøre databasebehandlinger om brukere.server.js
er for oppstart av prosjektet og forespørsel om håndtering.Det er det! Dette prosjektet er veldig enkelt, slik at du lett kan forstå hovedkonseptet uten å gjøre en dykk dykk.
"name": "angular-restful-auth", "versjon": "0.0.1", "avhengigheter": "express": "4.x", "body-parser": "~ 1.0.0" , "morgan": "nyeste", "mongoose": "3.8.8", "jsonwebtoken": "0.4.0", "motorer": "node": "> = 0.10.0"
package.json
inneholder avhengigheter for prosjektet: uttrykke
for MVC, body-parser
for å simulere etterbehandlingshåndtering i NodeJS, Morgan
for forespørsel logging, Mongoose
for vårt ORM-rammeverk for å koble til MongoDB, og jsonwebtoken
for å lage JWT-tokens ved å bruke vår brukermodell. Det er også et attributt som heter motorer
som sier at dette prosjektet er laget ved å bruke NodeJS versjonen> = 0.10.0. Dette er nyttig for PaaS-tjenester som Heroku. Vi vil også dekke det aktuelle emnet i en annen seksjon.
var mongoose = krever ('mongoose'); var Schema = mongoose.Scema; var UserSchema = nytt skjema (email: String, passord: String, token: String); module.exports = mongoose.model ('User', UserSchema);
Vi sa at vi ville generere et token ved å bruke brukermodellens nyttelast. Denne modellen hjelper oss med å gjøre brukeroperasjoner på MongoDB. I User.js
, bruker-skjemaet er definert og brukermodellen er opprettet ved hjelp av en mongoose-modell. Denne modellen er klar for databasebehandling.
Våre avhengigheter er definert, og vår brukermodell er definert, så la oss kombinere alle de som skal bygge en tjeneste for å håndtere spesifikke forespørsler.
// Obligatoriske moduler var express = krever ("express"); var morgan = krever ("morgan"); var bodyParser = krever ("body-parser"); var jwt = krever ("jsonwebtoken"); var mongoose = krever ("mongoose"); var app = express ();
I NodeJS kan du inkludere en modul i prosjektet ditt ved å bruke krever
. Først må vi importere de nødvendige modulene til prosjektet:
var port = process.env.PORT || 3001; var Bruker = krever ('. / modeller / bruker'); // Koble til DB mongoose.connect (process.env.MONGO_URL);
Vår tjeneste vil tjene gjennom en bestemt port. Hvis noen portvariabel er definert i systemmiljøvariablene, kan du bruke det, eller vi har definert port 3001
. Etter det er brukermodellen inkludert, og databasetilkoblingen er etablert for å gjøre noen brukeroperasjoner. Ikke glem å definere en miljøvariabel-MONGO_URL
-for nettadressen til databaseforbindelsen.
app.use (bodyParser.urlencoded (extended: true)); app.use (bodyParser.json ()); app.use (Morgan ( "dev")); app.use (funksjon (req, res, neste) res.setHeader ('Access-Control-Allow-Origin', '*'); res.setHeader ('Access-Control-Allow-Methods', 'GET, POST '); res.setHeader (' Access-Control-Allow-Headers ',' X-Requested-With, innholdstype, autorisasjon '); neste (););
I avsnittet ovenfor har vi laget noen konfigurasjoner for å simulere en HTTP-forespørselhåndtering i NodeJS ved å bruke Express. Vi tillater forespørsler om å komme fra forskjellige domener for å utvikle et klientuavhengig system. Hvis du ikke tillater dette, vil du utløse en CORS (Cross Origin Request Sharing Sharing) feil i nettleseren.
Tilgang-Control-La-Origin
tillatt for alle domener.POST
og FÅ
forespørsler til denne tjenesten.X-Requested-Med
og innholdstype
overskrifter er tillatt.app.post ('/ autentiser', funksjon (req, res) User.findOne (email: req.body.email, passord: req.body.password, funksjon (feil, bruker) hvis (err) res.json (type: false, data: "Feil oppstod:" + err); else if (user) res.json (type: true, data: user, token: user.token); else res.json (type: false, data: "Feil e-post / passord");;;);
Vi har importert alle nødvendige moduler og definert vår konfigurasjon, så nå er det på tide å definere forespørselsbehandlere. I ovennevnte kode, når du lager en POST
forespørsel til / godkjenne
med brukernavn og passord, vil du få en JWT
token. Først behandles databasespørsmålet ved å bruke et brukernavn og passord. Hvis en bruker eksisterer, returneres brukerdataene med sin token. Men, om det ikke finnes en slik bruker som matcher brukernavnet og / eller passordet?
app.post ('/ signin', funksjon (req, res) User.findOne (email: req.body.email, passord: req.body.password, funksjon (feil, bruker) hvis (feil) res.json (type: false, data: "Feil oppsto:" + err); annet hvis (bruker) res.json (type: false, data: "Brukeren eksisterer allerede!"); ellers var userModel = ny bruker (); userModel.email = req.body.email; userModel.password = req.body.password; userModel.save (funksjon (feil, bruker) user.token = jwt.sign (bruker , prosess.env.JWT_SECRET); user.save (funksjon (feil, bruker1) res.json (type: sann, data: bruker1, token: user1.token););)); );
Når du lager en POST
forespørsel til /Logg inn
med brukernavn og passord, vil en ny bruker bli opprettet ved å bruke lagt inn brukerinformasjon. På 19.
linje, kan du se at et nytt JSON-token er generert ved å bruke jsonwebtoken
modul, som har blitt tildelt til JWT
variabel. Autentiseringsdelen er OK. Hva om vi prøver å få tilgang til et begrenset sluttpunkt? Hvordan klarer vi å få tilgang til det endepunktet?
app.get ('/ meg', sikreAuthorized, funksjon (req, res) User.findOne (token: req.token, funksjon (feil, bruker) hvis (err) res.json , data: "Feil oppsto:" + err); else res.json (type: true, data: user);););
Når du lager en FÅ
forespørsel til /meg
, Du vil få den nåværende brukerinformasjonen, men for å fortsette med det ønskede sluttpunktet, vil ensureAuthorized
funksjonen vil bli utført.
funksjon sikreAuthorized (req, res, next) var bærerToken; var carryerHeader = req.headers ["autorisasjon"]; hvis (type av bærerHeader! == 'undefined') var bærer = bærerHeader.split (""); bærerToken = bærer [1]; req.token = bearerToken; neste (); ellers res.send (403);
I denne funksjonen blir forespørselhodene oppfanget og autorisasjon
header er hentet ut. Hvis det finnes et bærer-token i denne overskriften, er denne token tilordnet req.token
for å kunne brukes i hele forespørselen, og forespørselen kan videreføres ved bruk av neste ()
. Hvis et token ikke eksisterer, får du et 403 (Forbudt) svar. La oss gå tilbake til handleren /meg
, og bruk req.token
for å hente brukerdata med dette tokenet. Når du oppretter en ny bruker, genereres en token og lagres i brukermodellen i DB. Disse tokens er unike.
Vi har bare tre håndtere for dette enkle prosjektet. Etter det vil du se;
process.on ('uncaughtException', funksjon (err) console.log (err););
NodeJS-appen kan krasje hvis det oppstår en feil. Med den ovennevnte koden, er denne krasjen forhindret og en feillogg skrives ut i konsollen. Og til slutt kan vi starte serveren ved å bruke følgende kodestykke.
// Start server app.listen (port, funksjon () console.log ("Express server lytter på port" + port););
Å oppsummere:
Vi er ferdige med back-end-tjenesten. Slik at den kan brukes av flere kunder, kan du distribuere dette enkle serverprogrammet til serverne dine, eller kanskje du kan distribuere i Heroku. Det heter en fil Procfile
i prosjektets rotmappe. La oss distribuere vår tjeneste i Heroku.
Du kan klone bakprosjektet fra dette GitHub-depotet.
Jeg vil ikke diskutere hvordan du lager en app i Heroku; Du kan henvise til denne artikkelen for å opprette en Heroku-app hvis du ikke har gjort dette før. Etter at du har opprettet din Heroku-app, kan du legge til et mål for ditt nåværende prosjekt ved å bruke følgende kommando:
git fjernkontroll legg til heroku
Nå har du klonet et prosjekt og lagt til en destinasjon. Etter git add
og git commit
, Du kan trykke koden til Heroku ved å utføre git push heroku master
. Når du vellyver et prosjekt, vil Heroku utføre npm installasjon
beordre å laste ned avhengigheter i temp
mappe på Heroku. Deretter starter programmet og du kan få tilgang til tjenesten ved å bruke HTTP-protokollen.
I front-end-prosjektet vil du se et AngularJS-prosjekt. Her skal jeg bare nevne hoveddelene i front-end-prosjektet, fordi AngularJS ikke er noe som kan dekkes i en enkelt opplæring.
Du kan klone prosjektet fra dette GitHub-depotet. I dette prosjektet vil du se følgende mappestruktur:
ngStorage.js
er et bibliotek for AngularJS for å manipulere lokale lagringsoperasjoner. Det er også en hovedoppsett index.html
og partials som utvider hovedoppsettet under partials
mappe. controllers.js
er for å definere våre kontrollerhandlinger i fronten. services.js
er for å gjøre tjenesteforespørsler til vår tjeneste som jeg nevnte i det forrige prosjektet. Vi har en bootstrap-lignende fil som heter app.js
og i denne filen brukes konfigurasjoner og modulimport. Endelig, client.js
er for å betjene statiske HTML-filer (eller bare index.html
, i dette tilfellet); Dette hjelper oss med å betjene statiske HTML-filer når du distribuerer til en server uten å bruke Apache eller andre webservere.
...
I HTML-filen med hovedoppsett er alle nødvendige JavaScript-filer inkludert for AngularJS-relaterte biblioteker, i tillegg til vår egendefinerte kontroller, tjeneste og appfil.
'bruk streng'; / * Controllers * / angular.module ('angularRestfulAuth') .controller ('HomeCtrl', '$ rootScope', '$ scope', '$ location', '$ localStorage', 'Main' $ scope.password, $ location.password Main.signin (formData, funksjon (res) $ scope.signin = function () var formData = email: $ scope.email, hvis (res.type == false) alert (res.data) annet $ localStorage.token = res.data.token; window.location = "/";, funksjon () $ rootScope.error = 'Mislyktes å logge';); $ scope.signup = function () var formData = email: $ scope.email, passord: $ scope.password Main.save (formData, funksjon (res) if res.type == false) alert (res.data) annet $ localStorage.token = res.data.token; window.location = "/", funksjon () $ rootScope.error = 'Kunne ikke signup ';); $ scope.me = funksjon () Main.me (funksjon (res) $ scope.myDetails = res;, funksjon () $ rootScope.error =' Kunne ikke hente detaljer '; ); $ scope.logout = funksjon () Main.logout (fu nction () window.location = "/", funksjon () alert ("Kunne ikke logge ut!"); ); ; $ scope.token = $ localStorage.token; ])
I ovennevnte kode, den HomeCtrl
kontrolleren er definert og noen nødvendige moduler injiseres som $ rootScope
og $ omfang
. Dependensinjeksjon er en av de sterkeste egenskapene til AngularJS. $ omfang
er brovariabelen mellom kontroller og visninger i AngularJS som betyr at du kan bruke test
i visning hvis du definerte det i en bestemt kontroller som $ Scope.test = ...
I denne kontrolleren defineres noen bruksfunksjoner, for eksempel:
Logg inn
å sette opp en påloggingsknapp på påmeldingsskjemaetmelde deg på
for påmeldingsskjemahåndteringmeg
for å tilordne meg-knappen i oppsettetI hovedoppsettet, i hovedmenylisten, kan du se data-ng-kontrolleren
Tilordne med en verdi HomeCtrl
. Det betyr at denne menyen dom
element kan dele omfang med HomeCtrl
. Når du klikker på påmeldingsknappen i skjemaet, vil registreringsfunksjonen i kontrollerfilen bli utført, og i denne funksjonen blir sign-up-tjenesten brukt fra Hoved
tjeneste som allerede er injisert i denne kontrolleren.
Hovedstrukturen er vis -> kontroller -> tjeneste
. Denne tjenesten gjør enkle Ajax-forespørsler til back-end for å få bestemte data.
'bruk streng'; angular.module ('angularRestfulAuth') .factory ('Main', ['$ http', '$ localStorage', funksjon ($ http, $ localStorage) var baseUrl = "your_service_url"; funksjonsendringUser (bruker) vinkel. utvide (currentUser, user); funksjon urlBase64Decode (str) var output = str.replace ('-', '+'). erstatte ('_', '/'); bytte (output.length% 4) tilfelle 0: pause; tilfelle 2: utgang + = '=='; pause; tilfelle 3: utgang + = '='; pause; standard: kaste 'ulovlig base64url streng!'; returnere window.atob (utgang); funksjon getUserFromToken () var token = $ localStorage.token; var bruker = ; hvis (typeof token! == 'undefined') var kodet = token.split ('.') [1]; bruker = JSON. parse (urlBase64Decode (kodet)); retur bruker; var currentUser = getUserFromToken (); return lagre: funksjon (data, suksess, feil) $ http.post (baseUrl + '/ signin', data) .success suksess) .ror (feil), signin: funksjon (data, suksess, feil) $ http.post (baseUrl + '/ authenticate', data) .success (suksess) .ror (feil), meg: funksjon suksess, feil) $ htt p.get (baseUrl + '/me').success(success).error(error), logout: funksjon (suksess) changeUser (); slett $ localStorage.token; suksess(); ; ]);
I ovennevnte kode kan du se tjenestefunksjoner som å gjøre forespørsler om godkjenning. I controller.js har du kanskje allerede innsett at det finnes funksjoner som Main.me
. Dette Hoved
tjenesten er injisert i kontrolleren, og i kontrolleren blir tjenestene som tilhører denne tjenesten direkte kalt.
Disse funksjonene er bare Ajax-forespørsler til vår tjeneste som vi distribuerte sammen. Ikke glem å legge inn tjenesteadressen baseURL
i ovennevnte kode. Når du distribuerer tjenesten til Heroku, vil du få en tjenesteadresse som appname.herokuapp.com
. I koden ovenfor angir du var baseUrl = "appname.herokuapp.com"
.
I påmeldings- eller påloggingsdelen av søknaden svarer bærerstoken til forespørselen, og denne token lagres i lokal lagring. Når du gjør en forespørsel til en tjeneste i back-end, må du sette dette token i overskriftene. Du kan gjøre dette ved å bruke AngularJS interceptors.
$ httpProvider.interceptors.push ($ '$ q', '$ location', '$ localStorage', funksjon ($ q, $ plassering, $ localStorage) return 'request`: funksjon (config) config.headers = config.headers || ; if ($ localStorage.token) config.headers.Authorization = 'Bearer' + $ localStorage.token; return config;, 'responseError': funksjon (svar) if status === 401 || response.status === 403) $ location.path ('/ signin'); returnere $ q.reject (respons);;]);
I ovennevnte kode blir alle forespørsler avlyttet, og en autorisasjonsoverskrift og verdi legges i overskriftene.
I front-end-prosjektet har vi noen delvise sider som Logg inn
, melde deg på
, profildetaljer
, og vb
. Disse delvise sidene er relatert til bestemte kontroller. Du kan se det forholdet i app.js
:
angular.module ('angularRestfulAuth', ['ngStorage', 'ngRoute']) .config (['$ routeProvider', '$ httpProvider', funksjon ($ routeProvider, $ httpProvider) $ routeProvider. templateUrl: 'partials / home.html', kontroller: 'HomeCtrl'). når ('/ signin', templateUrl: 'partials / signin.html', kontroller: 'HomeCtrl'). ', templateUrl:' partials / signup.html ', kontroller:' HomeCtrl '). Når (' / meg ', templateUrl:' partials / me.html ' omdirigeringTo: '/');
Som du lett kan forstå i koden ovenfor, når du går til /
, de home.html
siden vil bli gjengitt. Et annet eksempel: hvis du går til /melde deg på
, signup.html
vil bli gjengitt. Denne gjengivelsesoperasjonen vil bli gjort i nettleseren, ikke på server-siden.
Du kan se hvordan alt vi diskuterte i denne opplæringen, fungerer i praksis ved å sjekke ut denne fungerende demoen.
Tokenbaserte autentiseringssystemer hjelper deg å konstruere et autentiserings- / autorisasjonssystem mens du utvikler klientuavhengige tjenester. Ved å bruke denne teknologien vil du bare fokusere på tjenestene dine (eller APIer).
Autentifikasjons- / autorisasjonsdelen vil bli håndtert av det tokenbaserte autentiseringssystemet som et lag foran tjenestene dine. Du kan få tilgang til og bruke tjenester fra enhver klient som nettlesere, Android, iOS eller en stasjonær klient.
Og hvis du leter etter ferdige løsninger, sjekk ut autentiseringsskriptene og -appene på Envato Market.