Pri razvoju spletne aplikacije je na splošno dobro, da jo razdelimo na dva nivoja. API srednjega nivoja komunicira z bazo podatkov, spletni nivo pa je običajno sestavljen iz čelnega SPA ali MPA. Na ta način je spletna aplikacija bolj ohlapno povezana, kar olajša upravljanje in odpravljanje napak na dolgi rok.
Ko je API ustvarjen, se lahko zdi preverjanje pristnosti in stanja v kontekstu API-ja brez stanja nekoliko težavno.
V tem članku bomo preučili, kako uporabiti popolno avtentikacijo uporabnika in preprosto obliko nadzora dostopa v API-ju z uporabo Laravel in Passport. Morali bi imeti izkušnje z delom Laravel saj to ni uvodna vadnica.
Predpogoji za namestitev:
composer require laravel/helpers
Z zgoraj nameščenimi smo pripravljeni začeti. Ne pozabite nastaviti povezave z bazo podatkov tako, da uredite .env
mapa.
Najprej bomo ustvarili krmilnik in model za navidezne zahteve. Model v tej vadnici ne bo veliko koristen, temveč le zato, da predstavi podatke, s katerimi naj bi krmilnik upravljal.
Pred izdelavo modela in krmilnika moramo ustvariti selitev. V terminalu - ali cmd.exe
okno, če uporabljate Windows - zaženite:
php artisan make:migration create_articles_table --create=articles
Zdaj pojdite na database/migrations
mapo in odprite datoteko z imenom, podobnim xxxx_xx_xx_xxxxxx_create_articles_table.php
.
V up
funkcijo razreda, bomo zapisali to:
Schema::create('articles', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->string('body'); $table->integer('user_id'); $table->timestamps(); });
Nato bomo ustvarili Article
model. Če želite to narediti, zaženite:
php artisan make:model Article
Nato ustvarimo ArticleController
krmilnik z zagonom:
php artisan make:controller ArticleController --resource
Nato bomo uredili datoteko app/Providers/AppServiceProvider.php
in uvozite IlluminateSupportFacadesSchema
razred z dodajanjem:
use IlluminateSupportFacadesSchema
... na dno uvoza na vrhu datoteke.
Nato v boot
funkcijo, napisali bomo:
Schema::defaultStringLength(191);
Po vsem tem lahko zaženemo:
php artisan migrate
… Za uporabo zgoraj ustvarjene selitve.
Tu bomo dodali koščke vmesne programske opreme, ki bodo potrebni za delovanje API-ja.
Prvi potreben kos je ForceJsonResponse
vmesna programska oprema, ki bo vse odgovore samodejno pretvorila v JSON.
Če želite to narediti, zaženite:
php artisan make:middleware ForceJsonResponse
In to je funkcija ročaja te vmesne programske opreme v App/Http/Middleware/ForceJsonReponse.php
:
public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); }
Nato bomo v našo app/Http/Kernel.php
datoteko dodali vmesno programsko opremo v datoteki $routeMiddleware
matrika:
'json.response' => AppHttpMiddlewareForceJsonResponse::class,
Nato ga bomo dodali tudi v $middleware
matrika v isti datoteki:
AppHttpMiddlewareForceJsonResponse::class,
Tako bi zagotovili, da ForceJsonResponse
vmesna programska oprema se zažene na vsako zahtevo.
Potrošnikom našega API Laravel REST za dostop do njega iz drugega izvora moramo nastaviti CORS. Za to bomo ustvarili del vmesne programske opreme, imenovan Cors
.
V terminalu ali ukaznem pozivu cd
v korenski imenik projekta in zaženite:
php artisan make:middleware Cors
Nato v app/Http/Middleware/Cors.php
dodajte naslednjo kodo:
public function handle($request, Closure $next) { return $next($request) ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, X-Token-Auth, Authorization'); }
Če želite naložiti ta del vmesne programske opreme, bomo morali dodati vrstico app/Http/Kernel.php
s $routeMiddleware
matrika:
'cors' => AppHttpMiddlewareCors::class,
Prav tako ga bomo morali dodati v $middleware
kot pri prejšnji vmesni programski opremi:
AppHttpMiddlewareCors::class,
Po tem bomo tej skupini poti dodali routes/api.php
:
Route::group(['middleware' => ['cors', 'json.response']], function () { // ... });
Kot bomo videli spodaj, bodo v to funkcijo vključene vse naše poti API.
Zdaj želimo ustvariti krmilnik za preverjanje pristnosti z login
in register
funkcije.
Najprej bomo izvedli:
php artisan make:controller Auth/ApiAuthController
Zdaj bomo v datoteko uvozili nekaj razredov app/Http/Controllers/Auth/ApiAuthController.php
. Ti razredi bodo uporabljeni pri ustvarjanju login
in register
funkcije. Razrede bomo uvozili tako, da bomo dodali:
use AppUser; use IlluminateSupportFacadesHash; use IlluminateSupportFacadesValidator; use IlluminateSupportStr;
... na vrh krmilnika.
Da bi za naše uporabnike dodali overjanje API-ja Laravel, bomo ustvarili login
, logout
in register
(prijava) deluje v isti datoteki.
The register
funkcija bo videti tako:
public function register (Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response(['errors'=>$validator->errors()->all()], 422); } $request['password']=Hash::make($request['password']); $request['remember_token'] = Str::random(10); $user = User::create($request->toArray()); $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); }
The login
funkcija je taka:
public function login (Request $request) { $validator = Validator::make($request->all(), [ 'email' => 'required|string|email|max:255', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response(['errors'=>$validator->errors()->all()], 422); } $user = User::where('email', $request->email)->first(); if ($user) { if (Hash::check($request->password, $user->password)) { $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); } else { $response = ['message' => 'Password mismatch']; return response($response, 422); } } else { $response = ['message' =>'User does not exist']; return response($response, 422); } }
In končno, logout
funkcija:
public function logout (Request $request) { $token = $request->user()->token(); $token->revoke(); $response = ['message' => 'You have been successfully logged out!']; return response($response, 200); }
Po tem moramo dodati login
, register
in logout
na naše poti, tj. znotraj skupine poti že v API-ju:
Route::group(['middleware' => ['cors', 'json.response']], function () { // ... // public routes Route::post('/login', 'Auth [email protected] ')->name('login.api'); Route::post('/register','Auth [email protected] ')->name('register.api'); Route::post('/logout', 'Auth [email protected] ')->name('logout.api'); // ... });
Na koncu moramo dodati HasApiToken
lastnost User
model. Pomaknite se do app/User
in se prepričajte, da imate:
use HasApiTokens, Notifiable;
... na vrhu razreda.
Če zaženemo strežnik aplikacij - tj., Zaženite php artisan serve
- in nato poskusite poslati GET
zahteva za pot /api/user
, prejeli bi sporočilo:
{ 'message': 'Unauthenticated.' }
To je zato, ker nismo overjeni dostop do te poti . Za zaščito nekaterih poti po vaši izbiri jih lahko dodamo v routes/api.php
takoj za Route::post
vrstice:
Route::middleware('auth:api')->group(function () { // our routes to be protected will go in here });
Preden nadaljujemo, bomo pot odjave dodali v auth:api
vmesna programska oprema, ker Laravel uporablja žeton za odjavo uporabnika - žeton, do katerega ni mogoče dostopati zunaj auth:api
vmesna programska oprema. Naše javnosti poti izgledajo tako:
Route::group(['middleware' => ['cors', 'json.response']], function () { // ... // public routes Route::post('/login', 'Auth [email protected] ')->name('login.api'); Route::post('/register', 'Auth [email protected] ')->name('register.api'); // ... });
Naše zaščiten poti pa so videti takole:
Route::middleware('auth:api')->group(function () { // our routes to be protected will go in here Route::post('/logout', 'Auth [email protected] ')->name('logout.api'); });
Zdaj se bomo pomaknili do ArticleController
ustvarili smo v app/Http/Controllers/ArticleController.php
in izbrišite create
in edit
metode v tem razredu. Po tem bomo vsaki preostali funkciji dodali naslednji del kode, nekoliko urejen:
$response = ['message' => ' function']; return response($response, 200);
Izpolnili bomo, kot je primerno. Na primer, update
funkcija bo imela to telo:
$response = ['message' => 'update function']; return response($response, 200);
Za registracijo uporabnika bomo poslali POST
zahteva za /api/register
z naslednjimi parametri: name
, email
(ki mora biti enolično), password
in password_confirmation
.
Ko je uporabnik ustvarjen, API vrne žeton, ki ga bomo uporabili pri nadaljnjih zahtevah kot sredstvo za preverjanje pristnosti.
Za prijavo bomo poslali POST
zahteva za /api/login
. Če so naše poverilnice pravilne, bomo na ta način dobili tudi žeton iz našega API-ja za prijavo v Laravel.
Žeton za pooblastitev, ki ga dobimo s te zahteve, lahko uporabimo, ko želimo dostopati do zaščitene poti. V programu Poštar ima zavihek »Pooblastitev« spustni meni, kjer lahko vrsto nastavite na »Žeton prinašalca«, nato pa lahko žeton preide v polje žetona.
Postopek je v Nespečnost .
Uporabniki cURL lahko naredijo enakovredno s predajo parametra -H 'Authorization: Bearer '
, pri čemer je žeton pooblastila, podan iz odgovora za prijavo ali registracijo.
Tako kot pri cURL, če razvijalci nameravajo API uporabljati z uporabo aksijev ali takšne knjižnice, lahko dodajo Authorization
glava z vrednostjo Bearer
.
Zdaj, ko je osnovno preverjanje pristnosti končano, je čas, da nastavite funkcijo ponastavitve gesla.
Za to lahko izberemo api_auth
imenik krmilnika, ustvarite nove krmilnike po meri in izvedite funkcijo; ali pa lahko urejamo krmilnike avten, ki jih lahko ustvarimo z Laravel. V tem primeru bomo uredili krmilnike pristnosti, saj je celotna aplikacija API.
Najprej bomo generirali avtomatske krmilnike z zagonom:
composer require laravel/ui php artisan ui vue --auth
Razred bomo uredili v app/Http/Controllers/Auth/ForgotPasswordController.php
in dodali ti dve metodi:
protected function sendResetLinkResponse(Request $request, $response) { $response = ['message' => 'Password reset email sent']; return response($response, 200); } protected function sendResetLinkFailedResponse(Request $request, $response) { $response = 'Email could not be sent to this email address'; return response($response, 500); }
Nato moramo nastaviti krmilnik, ki dejansko ponastavi geslo, zato se bomo pomaknili na app/Http/Controllers/Auth/ResetPasswordController.php
in preglasite privzete funkcije, kot je ta:
protected function resetPassword($user, $password) { $user->password = Hash::make($password); $user->save(); event(new PasswordReset($user)); } protected function sendResetResponse(Request $request, $response) { $response = ['message' => 'Password reset successful']; return response($response, 200); } protected function sendResetFailedResponse(Request $request, $response) { $response = 'Token Invalid'; return response($response, 401); }
Prav tako moramo v krmilnik uvoziti nekaj razredov, tako da dodamo:
use IlluminateAuthEventsPasswordReset; use IlluminateHttpRequest; use IlluminateSupportFacadesHash;
... na vrh krmilnika.
Spremenili bomo tudi, katero e-poštno obvestilo se uporablja, ker e-poštno obvestilo, ki je priloženo Laravelu, za avtorizacijo ne uporablja žetonov API. Ustvarimo lahko novo pod app/Notifications
z zagonom tega ukaza:
php artisan make:notification MailResetPasswordNotification
Datoteko bomo morali urediti app/Notifications/MailResetPasswordNotification.php
videti tako:
pageUrl = 'localhost:8080'; // we can set whatever we want here, or use .env to set environmental variables } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return ['mail']; } /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return IlluminateNotificationsMessagesMailMessage */ public function toMail($notifiable) { if (static::$toMailCallback) { return call_user_func(static::$toMailCallback, $notifiable, $this->token); } return (new MailMessage) ->subject(Lang::getFromJson('Reset application Password')) ->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.')) ->action(Lang::getFromJson('Reset Password'), $this->pageUrl.'?token='.$this->token) ->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')])) ->line(Lang::getFromJson('If you did not request a password reset, no further action is required.')); } /** * Get the array representation of the notification. * * @param mixed $notifiable * @return array */ public function toArray($notifiable) { return [ // ]; } }
Če želimo uporabiti to novo obvestilo, moramo preglasiti sendPasswordResetNotification
metoda, ki User
podeduje od Authenticatable
razred. Vse, kar moramo storiti, je dodati to app/User.php
:
public function sendPasswordResetNotification($token) { $this->notify(new AppNotificationsMailResetPasswordNotification($token)); }
Ob pravilno delujoči nastavitvi pošte bi morala obvestila v tem trenutku delovati.
Zdaj je le še nadzor dostopa uporabnika.
Preden ustvarimo vmesno programsko opremo za nadzor dostopa, bomo morali posodobiti user
tabela, da ima stolpec type
, ki se bo uporabljal za določanje ravni uporabnika: tip 0 je običajen uporabnik, tip 1 je skrbnik in tip 2 je super-skrbnik.
Če želite posodobiti user
tabelo, moramo ustvariti migracijo tako, da zaženemo to:
php artisan make:migration update_users_table_to_include_type --table=users
V novo ustvarjeni datoteki obrazca database/migrations/[timestamp]_update_users_table.php
bomo morali posodobiti up
in down
funkcije za dodajanje in odstranjevanje type
stolpec:
public function up() { Schema::table('users', function (Blueprint $table) { $table->integer('type'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropIfExists('type'); }); }
Nato bomo zagnali php artisan migrate
. Ko je to končano, moramo urediti naš register
v ApiAuthController.php
datoteko, doda to tik pred vrstico z $user = User::create($request->toArray());
:
$request['type'] = $request['type'] ? $request['type'] : 0;
To vrstico bomo morali dodati tudi v $validator
matrika:
'type' => 'integer',
S prvim od teh dveh sprememb bodo vsi registrirani uporabniki privzeto »običajni uporabniki«, tj. Če ni vnesena nobena vrsta uporabnika.
Zdaj smo sposobni ustvariti dva dela vmesne programske opreme za nadzor dostopa: enega za skrbnike in enega za super-skrbnike.
Torej bomo izvedli:
php artisan make:middleware AdminAuth php artisan make:middleware SuperAdminAuth
Najprej se bomo pomaknili na app/Http/Middleware/AdminAuth.php
in uvozite IlluminateSupportFacadesAuth
, nato uredite handle
deluje tako:
public function handle($request, Closure $next) { if (Auth::guard('api')->check() && $request->user()->type >= 1) { return $next($request); } else { $message = ['message' => 'Permission Denied']; return response($message, 401); } }
Urejati bomo morali tudi handle
funkcija v app/Http/Middleware/SuperAdminAuth.php
:
public function handle($request, Closure $next) { if (Auth::guard('api')->check() && $request->user()->type >= 2) { return $next($request); } else { $message = ['message' => 'Permission Denied']; return response($message, 401); } }
Prav tako morate uvoziti Auth
razred na vrhu obeh datotek, tako da dodate:
use IlluminateSupportFacadesAuth;
... na dno tam najdenega uvoza.
Za uporabo naše nove vmesne programske opreme se bomo v jedru sklicevali na oba razreda, tj. V app/Http/Kernel.php
, tako da bomo dodali naslednje vrstice v $routeMiddleware
matrika:
'api.admin' => AppHttpMiddlewareAdminAuth::class, 'api.superAdmin' => AppHttpMiddlewareSuperAdminAuth::class,
Če želijo razvijalci vmesno programsko opremo uporabiti na določeni poti, jo morate le dodati v funkcijo poti, kot je ta:
Route::post('route',' [email protected] ')->middleware('');
v tem primeru je lahko po potrebi api.admin
, api.superAdmin
itd.
To je vse, kar je potrebno za ustvarjanje naše vmesne programske opreme.
Obstaja nekaj dodatnih korakov, da preverimo, ali naša preverjanje pristnosti in nadzor dostopa deluje.
Spremeniti moramo ArticleController
s index
in registrirajte pot. (V resničnih projektih bi uporabili PHPUnit in to storili kot del avtomatiziranega testa. Tu ročno dodajamo pot za namene testiranja - kasneje jo je mogoče odstraniti.)
Pomikali se bomo do ArticleController
krmilnik na app/Http/Controllers/ArticleController
in spremenite index
funkcija, ki bo videti tako:
public function index() { $response = ['message' => 'article index']; return response($response, 200); }
Nato bomo funkcijo registrirali na poti tako, da odpremo routes/api.php
datoteko in doda to:
Route::middleware('auth:api')->group(function () { Route::get('/articles', ' [email protected] ')->name('articles'); });
Zdaj lahko poskusimo dostopati do poti brez žetona za preverjanje pristnosti. Morali bi dobiti napako pri preverjanju pristnosti.
Do iste poti lahko poskusimo dostopati tudi z žetonom za avtorizacijo (tistim, ki smo ga dobili pri registraciji ali prijavi prej v tem članku).
Včasih lahko to povzroči napako, podobno tej:
Unknown column 'api_token' in 'where clause' (SQL: select * from `users` where `api_token` = ...
Če se to zgodi, bi morali razvijalci poskrbi da ste izvedli migracijo Passport in imeli ['guards']['api']['driver']
nastavljeno na passport
v config/auth.php
:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ],
Po tem je treba posodobiti tudi konfiguracijski predpomnilnik.
Ko bomo to uredili, bi morali imeti dostop do poti.
Čas je, da preizkusite nadzor dostopa. Priložimo ->middleware('api.admin')
do poti do člankov, zato je videti tako:
Route::get('/articles', ' [email protected] ')->middleware('api.admin')->name('articles');
Naredili smo tako, da se novo ustvarjenemu uporabniku samodejno dodeli tip 0, kot lahko vidimo preko api/user
poti.
Zaradi tega bi prišlo do napake pri poskusu dostopa do articles
končna točka kot tak uporabnik.
Za namen testiranja spremenimo uporabnika v zbirki podatkov tako, da ima type
od 1. Po preverjanju spremembe preko api/user
Ponovno pot smo pripravljeni poskusiti znova do GET
/articles/
poti.
Odlično deluje.
Razvijalci, ki izdelujejo bolj zapletene aplikacije, morajo vedeti, da ustrezen nadzor dostopa ne bo tako preprost. V tem primeru druge aplikacije tretjih oseb ali Laravel vrata in politike se lahko uporablja za izvajanje nadzora uporabniškega dostopa po meri. V drugem delu te serije bomo preučili bolj robustne in prilagodljive rešitve nadzora dostopa.
V tej vadnici Laravel Passport smo razpravljali o:
To so bistvene spretnosti za vsakogar, ki dela na tem področju Storitve razvoja Laravel . Bralci bodo končni rezultat našli v ta repo za GitHub in bi moral biti zdaj v dobrem položaju za izvajanje preverjanja pristnosti z Laravel. Veselimo se spodnjih komentarjev.
Laravel Passport je strežniška izvedba OAuth 2.0 za overjanje API-jev z uporabo Laravel-a. Ker se žetoni običajno uporabljajo pri overjanju API-jev, Laravel Passport ponuja preprost in varen način za izvajanje pooblastila žetonov na strežniku OAuth 2.0.
Laravel Passport je paket, ki se uporablja za izvajanje preverjanja pristnosti v API-ju Laravel REST.
Laravel Passport je strežniška izvedba OAuth 2.0 za avtentikacijo brez državljanstva. OAuth 2.0 je najnovejši protokol OAuth in ja, varen je.
API brez državljanstva je tisti, pri katerem je vsaka zahteva zanj popolnoma izolirana in je odgovor vsake zahteve popolnoma odvisen samo od zahteve. To je drugače kot pri aplikaciji s stanjem, kjer je odziv vsake zahteve odvisen od stanja strežnika in zahteve.