Hvis du bygger et CMS, trenger du sannsynligvis forskjellige brukerroller-superuser, admins, brukere - med forskjellige tillatelsesnivåer. For komplisert å kode? Angi CakePHPs ACL (Access Control Lists). Med riktig oppsett kontrollerer du brukerrettigheter med bare én linje.
ACL lar deg lage et hierarki av brukere med sine respektive roller. Her er et raskt eksempel.
I denne opplæringen vil vi sette opp en ACL for en enkel blogg. Hvis du ennå ikke har sjekket ut Komme i gang med CakePHP (og del 2) her på Nettuts +, vær så snill og vær så snill og kom tilbake, da vi vil ta for gitt rammebasertene.
Med dette hierarkiet kan vi tildele flere tillatelser for hver rolle:
Hver tillatelse vil bli gitt til gruppen, ikke til brukeren; så hvis bruker # 6 blir forfremmet til Admin, vil han bli sjekket mot gruppens tillatelse - ikke hans. Disse roller og barnnoder (brukere) kalles tilgangsforespørslerobjekter, eller AROer.
På den andre siden har vi tilgangskontrollobjekter, eller ACOer. Dette er objektene som skal kontrolleres. Over nevnte jeg innlegg og brukere. Normalt er disse objektene direkte knyttet til modellene, så hvis vi har en Post-modell, trenger vi en ACO for denne modellen.
Hver ACO har fire grunnleggende tillatelser: opprett, les, oppdater og slett. Du kan huske dem med søkeordet CRUD. Det er en femte tillatelse, stjernen, det er en snarvei for full tilgang.
Vi vil bare bruke to ACOer for denne opplæringen: Innlegg og bruker, men du kan opprette så mange som du trenger.
La oss fortsette å lage databasetabellene. Du kan finne denne koden i db_acl.sql
inne i appen din config / sql
katalog.
CREATE TABLE acos (ID INTEGER (10) UTSIGNERT IKKE NULL AUTO_INCREMENT, parent_id INTEGER (10) DEFAULT NULL, modell VARCHAR (255) DEFAULT ", foreign_key INTEGER (10) UNSIGNED DEFAULT NULL, alias VARCHAR (255) DEFAULT", lft INTEGER 10) DEFAULT NULL, rght INTEGER (10) DEFAULT NULL, PRIMARY KEY (id)); CREATE TABLE aros_acos (id INTEGER (10) UTSIGNERT IKKE NULL AUTO_INCREMENT, aro_id INTEGER (10) UTSIGNERT IKKE NULL, aco_id INTEGER (10) UTSIGNERT IKKE NULL, _create CHAR (2) IKKE NULL DEFAULT 0, _read CHAR (2) IKKE NULL DEFAULT 0, _update CHAR (2) IKKE NULL DEFAULT 0, _delete CHAR (2) IKKE NULLVALT 0, PRIMARY KEY (id)); CREATE TABLE aros (ID INTEGER (10) UTSIGNERT IKKE NULL AUTO_INCREMENT, parent_id INTEGER (10) DEFAULT NULL, modell VARCHAR (255) DEFAULT ", foreign_key INTEGER (10) UNSIGNED DEFAULT NULL, alias VARCHAR (255) DEFAULT", lft INTEGER 10) DEFAULT NULL, rght INTEGER (10) DEFAULT NULL, PRIMARY KEY (id));
Vi kan begynne å lage ARO og ACO noder, men hei, vi har ikke brukere! Vi må opprette et grunnleggende godkjenningssystem.
Siden denne opplæringen er beregnet for CakePHP-utviklere med en grunnleggende til moderat kunnskap om rammen, vil jeg levere koden og en kort forklaring. Autentiseringssystemet er imidlertid ikke målet med denne opplæringen.
MySQL-tabellen:
CREATE TABLE-brukere (ID INTEGER (10) UTSIGNERT AUTO_INCREMENT KEY, brukernavn TEXT, passord TEXT);
Brukermodellen (modeller / user.php
)
Brukerens kontroller (styringer / users_controller.php
)
Auth-> userModel = 'Bruker'; $ Dette-> Auth-> tillate ( '*'); funksjonsregister () hvis (! tomt ($ this-> data)) // Her bør du validere brukernavnet (min lengde, maks lengde, ikke å inkludere spesielle tegn, ikke eksisterende allerede osv.) som passordet hvis ($ this-> User-> validates ()) $ this-> User-> save ($ this-> data); // La oss lese dataene vi nettopp har satt inn $ data = $ this-> User-> read (); // Bruk den til å autentisere brukeren $ this-> Auth-> login ($ data); // Deretter omdirigere $ this-> omdirigering ('/'); funksjonslogg () hvis ! tom ($ this-> data)) // Hvis brukernavnet / passordet samsvarer med ($ dette-> Auth-> logg inn ($ this-> data)) $ dette -> viderekobling ( '/'); ellers $ this-> User-> invalidate ('brukernavn', 'Brukernavn og passord kombinasjon er feil!'); funksjon logout () $ this-> Auth-> logout (); $ Dette-> omdirigere ( '/'); ?>
Siden vi har nye elementer, la oss se gjennom dem. Først setter vi inn en $ komponenter
variabel. Denne variabelen inneholder alle komponentene i gruppen. Vi kommer til å trenge Auth komponent, som er en kjernebestanddel, som er HTML og Form-hjelpere, men siden det ikke er inkludert som standard av Cake, må vi inkludere det manuelt.
Auth-komponenten håndterer noen grunnleggende godkjenningsmekanikk: det hjelper oss å logge inn på en bruker, og den håndterer en godkjent brukerens økt for oss, samt håndteringen av loggen og grunnleggende autorisasjon for gjester. Dessuten har det passordet automatisk. Jeg skal forklare hvordan du skal ringe hver funksjon i de følgende avsnittene.
Deretter oppretter vi en funksjon som kalles beforeFilter
. Dette er en tilbakeringingsfunksjon, og det lar oss sette noen handlinger før alle kontrollerlogikkene behandles. Auth-komponenten krever at vi spesifiserer en modell som skal brukes, i dette tilfellet brukeren. Da vil det som standard nekte all tilgang til brukere som ikke er logget inn. Vi må overskrive denne oppførselen med tillate()
som krever en parameter. Den parameteren kan være en stjerne, og angir at alle metoder innenfor kontrolleren kan nås av uautoriserte brukere. Eller det kan bestå en matrise med funksjonene som kan nås av uautoriserte brukere. I dette tilfellet, siden vi bare har tre funksjoner, er følgende linjer det samme.
$ Dette-> Auth-> tillate ( '*'); $ this-> Auth-> allow (array ('register', 'login', 'logout'));
For Logg Inn()
funksjonen, vil Auth-komponenten håndtere alle innloggingsmekanismer for oss. Vi må bare levere funksjonen med en matrise med to nøkler: brukernavnet og passordet. Disse tastene kan endres, men som standard begge deler brukernavn
og passord
feltene vil bli matchet mot databasen og vil returnere sant hvis brukeren har blitt autentisert.
Endelig vil kontrollerens innloggingsfunksjon forsøke å matche et brukernavn / passord kombinasjon mot databasen.
Vær oppmerksom på at denne koden er veldig grunnleggende. Du må validere brukernavnene, hvis brukernavnet eksisterer, minimumslengden for passordet og så videre.
Register-visningen (visninger / brukere / register.ctp
)
Registrer din konto
Innloggingsvisningen (visninger / brukere / login.ctp
)
Logg inn på kontoen din
Åpen / Brukere / register
i nettleseren din og registrer en ny konto. jeg foreslår admin
som brukernavn og 123
som passord, og hvis sesjonen utløper, gå bare til / Brukere / login
og skriv inn riktig brukernavn / passord kombinasjon du nettopp opprettet.
Vi jobber ikke engang med ACL, men vi kan allerede nekte å legge inn, redigere og slette innlegg. Åpne Innleggskontrolleren din og legg til Auth-komponenten.
var $ components = array ('Auth');
Gå nå til / innlegg
i nettleseren din. Hvis du er logget inn, bør du se innleggene, men hvis du ikke er det, blir du omdirigert til / Brukere / login
. Ved å bare inkludere Auth-komponenten er alle handlingene som standard nektet for gjester. Vi må nekte tre handlinger for uautoriserte brukere: opprett, rediger og slett. I andre tilfeller må vi tillate indeks og visning.
funksjon beforeFilter () $ this-> Auth-> userModel = 'Bruker'; $ this-> Auth-> allow (array ('index', 'view'));
Gå til å redigere eller opprette et innlegg; Hvis du ikke er logget inn, bør du bli omdirigert til / Brukere / login
. Alt ser ut til å fungere ganske bra, men hva med visningene? Rediger og slett linkene blir vist til alle. Vi bør gjøre en betinget.
Men før vi går inn i det, la oss se hvordan Auths bruker () -funksjon fungerer. Kopier og lim inn disse linjene i indeksfunksjonen.
$ user = $ this-> Auth-> user (); pr ($ bruker);
Åpne din / innlegg
i nettleseren din og, hvis du er logget inn, pr ()
vil kaste noe sånt.
Array ([User] => Array ([id] => 1 [brukernavn] => admin))
De bruker()
funksjonen returnerer en matrise akkurat som en modell ville gjøre. Hvis vi hadde mer enn tre felt (passord er ikke inkludert), vil de bli vist i matrisen. Hvis du ikke er logget inn, vil arrayet være tomt, så du kan vite at en bruker er logget inn hvis Auth er bruker()
array er ikke tomt.
Nå er Auth en komponent, ment å brukes i en kontroller. Vi må vite fra en visning hvis en bruker er logget inn, helst via en hjelper. Hvordan kan vi bruke en komponent inne i en hjelper? CakePHP er så fantastisk og fleksibel at det er mulig.
class AccessHelper extends Helper var $helpers = array("Session"); function isLoggedin() App::import('Component', 'Auth'); $auth = new AuthComponent(); $auth->Session = $ this-> Session; $ user = $ auth-> user (); return! tom ($ bruker); ?>
Lagre denne koden i visninger / hjelpere
som access.php
. La oss nå se koden, linje for linje. Først setter vi opp en $ hjelpere
var. Hjelperne kan inkludere andre hjelpere, akkurat som $ komponenter
kan. Session-komponenten er nødvendig for Auth-komponenten, men vi har ingen tilgang til denne komponenten inne i en hjelper. Heldigvis har vi en Session hjelper, som vil hjelpe oss.
Deretter lager vi en funksjon og bruk App :: import
som vil la oss importere et element som vi normalt ikke ville ha tilgang til. Neste linje oppretter Auth-komponenten i en $ auth
variabel, og nå litt skitten hack; siden Auth-komponenten leser økten for å vite om vi er logget eller ikke, krever det sesjonskomponenten, men da vi importerer den fra et sted den ikke skal tilhøre, må vi gi den et nytt sesjonsobjekt. Til slutt bruker vi bruker()
og sette den til $ user
og returnerer sant hvis variabelen ikke er tom, ellers feil.
La oss komme tilbake til kontrollpanelet og fortsette å legge til hjelperen.
var $ helpers = array ('Access');
Tilgangshjelpen er nå tilgjengelig fra utsikten. Åpen index.ctp
i visninger / innlegg
og erstatt denne linjen.
"> redigere | echo $html->link ('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Er du sikker?'); ?>
Med denne.
if($access->isLoggedin ()):?>"> redigere | echo $html->link ('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Er du sikker?'); ?> endif; ?>
Gå tilbake til nettleseren din, last inn indekssiden, og hvis du er logget inn, vil du se redigering og slette koblinger for hvert innlegg. Ellers ser du ingenting.
Selv om dette ville være nok hvis du har en app med en eller to brukere, er det ikke nok hvis du har registreringer åpne.
Åpne brukerens kontroller og legg til ACL-komponenten.
var $ components = array ('Auth', 'Acl');
Deretter la vi lage en funksjon for å installere ACOs og AROs noder.
funksjonsinstallasjon () if ($ this-> Acl-> Aro-> findByAlias ("Admin")) $ this-> omdirigere ('/'); $ aro = new aro (); $ Aro-> opprette (); $ aro-> save (array ('model' => 'Bruker', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Super')); $ Aro-> opprette (); $ aro-> save (array ('model' => 'Bruker', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Admin')); $ Aro-> opprette (); $ aro-> save (array ('model' => 'Bruker', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Bruker')); $ Aro-> opprette (); $ aro-> save (array ('model' => 'Bruker', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Suspended')); $ aco = ny Aco (); $ ACO> opprette (); $ aco-> save (array ('model' => 'Bruker', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Bruker')); $ ACO> opprette (); $ aco-> save (array ('model' => 'Post', 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Post')); $ this-> Acl-> allow ('Super', 'Post', '*'); $ this-> Acl-> allow ('Super', 'User', '*'); $ this-> Acl-> allow ('Admin', 'Post', '*'); $ this-> Acl-> allow ('User', 'Post', array ('create'));
Ved å importere ACL-komponenten, kan vi få tilgang til ACOs og AROs-modellen. Vi begynner med å lage en ARO, som er søkeren til objektene; med andre ord, hvem vil få tilgang til objektene. Det ville være brukeren (dermed brukerstrengen i modellen). Vi skaper ulike roller; Super, Admin, Bruker og Suspended.
Deretter gjør vi det samme med ACOer, siden vi bare har to objekter å administrere (Innlegg og brukere), oppretter vi to, en for hver.
Nå, et raskt notat. Arrayet som vi lagrer for både ACOs og AROs har fire felt: modellnavnet, utenlandsk nøkkel (som er nyttig hvis du vil gi tilgang til en ARO, som et innlegg han opprettet), foreldre id (som vil bli brukt senere og er det grunnleggende foreldre-barn-nodeforholdet, vi skal skape brukere under disse rollene), og aliaset (som er en rask form for å finne objektene).
Til slutt bruker vi ACLs tillate()
funksjon. Den første parameteren er ACO-aliaset; For det andre, ARO-aliaset, og tredje, tillatelsene gitt til ARO. En Superbruker har full tilgang til Post- og brukermodellene, en administrator har full tilgang til innleggsmodellen, og en bruker kan bare opprette innlegg.
Ved funksjonens begynnelse, erklærte jeg en betingelse for å kontrollere om adminrollen eksisterer i ACOer. Du vil ikke installere det samme mer enn en gang, gjør du? Det ville ødelegge databasen ganske dårlig.
Åpen / brukere / installere
i nettleseren din, og siden vi ikke har en visning, vil CakePHP kaste en feil, men bare sjekk MySQL-dumpen. Alle relasjonene har blitt opprettet, det er på tide å jobbe med barnnodeene.
La oss rengjøre brukere
bord. Åpne phpMyAdmin, velg databasen din, brukere
bord og klikk tomt. Vi kommer tilbake til registrere()
Funksjon på Brukerkontrolleren. Like under denne linjen:
$ Dette-> Auth-> innloggings ($ data);
Lim inn denne koden:
// Sett brukerrollene $ aro = ny Aro (); $ foreldre = $ aro-> findByAlias ($ this-> User-> find ('count')> 1? 'Bruker': 'Super'); $ Aro-> opprette (); $ aro-> save (array ('model' => 'Bruker', 'foreign_key' => $ this-> Bruker-> id, 'parent_id' => $ foreldre ['Aro'] ['id'], ' alias '=>' Bruker :: '. $ dette-> Bruker-> ID));
I første linje oppretter vi et nytt ARO-objekt. Da får vi den overordnede noden der brukeren skal opprettes. Hvis det er en post i databasen, setter vi den til Bruker ARO, ellers bør den første brukeren være Super.
Da lagrer vi en ny ARO; modellen er den vi jobber for tiden, Bruker
, de fremmed
er den siste postens id vi nettopp har laget. De PARENT_ID
er noden vi begynte med; Vi sender bare det id og til slutt aliaset. Det er en god ide å kalle det Modell navn
, så en separator ::
, og deretter identifikatoren. Det vil bli mye lettere å finne det.
Nå er vi ferdige. Opprett fire brukere: superbruker, administrator, normalbruker og suspenderingsbruker. Jeg foreslår det samme brukernavnet og passordet for testing. Ikke glem det, til dette punktet har bare superbrukeren en rolle som Super; Alle de resterende blir Brukere!
Fordi ACL er en komponent, er den bare tilgjengelig i kontrolleren. Senere vil det også være i utsikten; men først ting først. Inkluder ACL-komponenten i Innlegg-kontrolleren, som vi gjorde i brukerens kontroller. Nå kan du begynne å sjekke tillatelser. La oss gå til redigeringsfunksjonen og gjøre en rask test. I den første linjen av metoden, legg til dette.
$ user = $ this-> Auth-> user (); Hvis (! $ this-> Acl-> sjekk ('Bruker ::'. $ bruker ['Bruker'] ['id'], 'Post', 'oppdatering')) dør ('du er ikke autorisert');
ACL er kryss av()
funksjonen trenger tre parametere: ARO, ACO og handlingen. Gå og rediger et innlegg, og hvis du ikke er logget inn som superbruker, vil skriptet dø. Gå til / Brukere / login
og tilgang som Super-bruker og gå tilbake til redigering. Du bør kunne redigere innlegget. Sjekk under MySQL-dumpen for å se den magiske. Fire database spørringer: det sikker er ikke skalerbar.
Vi har to problemer. Først, to linjer for tillatelseskontrollen. For det andre blir ikke ACLene bufret. Og ikke glem alle de loggede brukerne kan se redigeringslinken, selv om bare Super-brukeren kan bruke den.
La oss lage en ny komponent. La oss kalle det Access.
bruker = $ dette-> Auth-> bruker (); ?>
Lagre det i Styringer / komponenter
som access.php
. Klassen er $ user
Var vil bli startet ettersom komponenten er lastet, og oppstart ()
er en tilbakeringingsfunksjon, så den er nå satt i klassen. La oss nå lage vår kryss av()
funksjon.
funksjonskontroll ($ aco, $ action = '*') hvis (! tom ($ denne-> bruker) && $ dette-> Acl-> sjekk ('Bruker ::'. $ this-> bruker ['Bruker' ] ['id'], $ aco, $ action)) return true; ellers return false;
Våre kryss av()
Metoden trenger bare to parametre: ACO og handlingen (som er valgfritt). ARO vil være den nåværende brukeren for hver økt. Handlingsparameteret blir standard til *
, som er full tilgang til ARO. Funksjonen begynner med å sjekke om $ Dette-> user
er ikke tomt (som virkelig forteller oss om en bruker er logget inn) og så går vi til ACL. Vi har allerede dekket dette.
Vi kan nå inkludere Access-komponenten i vår Innlegg-kontroller og sjekke tillatelsene med bare én linje.
hvis (! $ this-> Access-> sjekk ('Post', 'update')) dø ('du er ikke autorisert');
Det samme resultatet oppnås med mindre kode, men feilmeldingen er stygg. Du bør bedre erstatte dø()
med CakePHP feilhåndterer:
$ Dette-> cakeError ( 'error404');
Dette kan være stygg, men det fungerer. Vi må opprette en hjelper som laster en komponent med en tilpasset metode for bruk i hjelperen.
Legg til denne funksjonen i tilgangskomponenten (styringer / komponenter / access.php
).
funksjon checkHelper ($ aro, $ aco, $ action = "*") App :: import ('Component', 'Acl'); $ acl = ny AclComponent (); returner $ acl-> sjekk ($ aro, $ aco, $ action);
La oss nå skrive om tilgangshjelpen (visninger / hjelpere / access.php
).
Access = ny AccessComponent (); App :: import ('Komponent', 'Auth'); $ this-> Auth = ny AuthComponent (); $ this-> Auth-> Session = $ this-> Session; $ this-> user = $ this-> Auth-> user (); funksjonskontroll ($ aco, $ action = '*') hvis (tomt ($ denne-> bruker)) returnerer false; returnere $ this-> Access-> checkHelper ('User ::'. $ this-> bruker ['Bruker'] ['id'], $ aco, $ action); funksjon isLoggedin () return! empty ($ this-> bruker); ?>
De beforeRender ()
Metoden er tilbakering, ligner komponentens oppstart ()
. Vi laster inn to komponenter, og siden disse blir brukt i de fleste funksjoner, er det en god idé å starte alt på en gang, enn å starte dem manuelt hver gang metoden heter.
Nå på din index.ctp
se på visninger / innlegg
Du kan erstatte denne linjen.
"> redigere | echo $html->link ('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Er du sikker?'); ?>
Med denne.
if($access->sjekk ('Post')):?>"> redigere | echo $html->link ('delete', '/posts/delete/'.$post['Post']['id'], NULL, 'Er du sikker?'); ?> endif; ?>
Ikke glem å sjekke tillatelser, både i visningene og kontrollerne!
Du kan få tilgang til brukerdataene for en logget bruker med bruker()
metode i Auth-komponenten. Deretter kan du få tilgang til arrayet og få den informasjonen du vil ha. Men det må være en bedre måte. La oss legge til følgende funksjon i Access-komponenten.
funksjon getmy ($ hva) return! empty ($ this-> bruker) && isset ($ this-> user ['User'] [$ what])? $ this-> user ['User'] [$ what]: false;
Dette er ganske nyttig når du trenger å lagre et innlegg med en bruker-ID
forhold.
$ this-> data ['Post'] ['user_id'] = $ this-> Tilgang-> getmy ('id');
Og i utsikten kan vi gjøre noe lignende med hjelperen.
funksjon getmy ($ hva) return! empty ($ this-> bruker) && isset ($ this-> user ['User'] [$ what])? $ this-> user ['User'] [$ what]: false;
I malfilen kan du gjøre noe som under for å hilse på en bruker etter brukernavnet.
Velkommen =$access->isLoggedIn ()? $ access-> getmy ('brukernavn'): 'Guest'; ?>
La oss si at vi må bytte roller til brukeren # 4: han må være en superbruker. Så, brukerens ID er 4 og Aros ID er 1.
$ user_id = 4; $ user_new_group = 1;
Nå må vi finne brukerens Aro for å kunne endre sin overordnede Aro.
$ aro_user = $ this-> Acl-> Aro-> finn ('first', array ('conditions' => array ('Aro.parent_id! =' => NULL, 'Aro.model' => 'Bruker' 'Aro.foreign_key' => $ user_id)));
Endelig, hvis $ aro_user
variabel er ikke tom, la oss oppdatere Aro.parent_id
felt.
hvis (! tom ($ aro_user)) $ data ['id'] = $ aro_user ['Aro'] ['id']; $ data ['parent_id'] = $ user_new_group; $ Dette-> Acl-> Aro-> Lagre ($ data);
Vær oppmerksom på at du må validere både brukerens id og den nye aro-iden. Hvis en av disse ikke eksisterer, vil det skape et rot i dine ACL-tabeller.
Selv om komponenten vi nettopp har opprettet er både enkel og nyttig, blir databasen forespurt med hver sjekk. Det er ikke klar for produksjon ennå. For det første bør databasen spørres så lite som mulig. For å optimalisere dette, bør vi dra nytte av CakePHPs Cache.
Ved hjelp av Cache reduseres belastningen ganske mye, men hvis vi har ti innlegg som vises i indeksen, og for hver enkelt bruker vi å kontrollere om brukeren har oppdateringsrettigheter for Post Aco, vil rammene lese og analysere en fil for å returnere det samme resultatet ... ti ganger for hver side lastes.
Det er det andre punktet: En variabel i klassen og noen conditionals vil gjøre arbeidet lettere, slik at en gjentatt forespørsel vil sjekke mot Cache bare en gang.
Begge disse endringene gjenspeiles i access_cache.php
, inne i Styringer / komponenter
katalogen. Så sørg for at du laster ned kilden!
Tilgangsstyrelister er en grunnleggende funksjon som de fleste apper trenger. CakePHP har en fin implementering, men mangler både god dokumentasjon og eksempler. Jeg håper at med disse opplæringen vil disse to problemene nå bli dekket. Takk for at du leste!