Jak nastavit REST API založené na Symfony pomocí OAuth

Chcete tedy nastavit REST API založené na Symfony… Dovolte nám, abychom vám poskytli několik tipů, jak postupovat.

SensioLabs, tvůrci společnosti Symfony, to popisují jako „soubor komponent PHP, rámec webových aplikací, filozofii a komunitu - vše společně harmonicky. Je jen přirozené, že se do toho pustíte! Tento tutoriál vám ukáže, jak nastavit REST API založené na Symfony pomocí protokolu OAuth jako autorizačního protokolu.

Pěkně popořádku

Budete muset nainstalovat prostředí Symfony. Oficiální dokumentace popisuje tento proces a vysvětluje, jak řešit běžné problémy.

Použili jsme nejnovější stabilní verzi v době, kdy jsme psali, což je Symfony 3.3.10. Můžete také nastavit Apache nebo Nginx VirtualHost, abyste k vaší aplikaci měli přístup z webového serveru. Nejlepší způsob, jak toho dosáhnout, je sledovat oficiální dokumenty.

A zkuste získat přístup k aplikaci voláním hostitele v prohlížeči:

Zatím je vše dobré…

Pojďme se vypořádat s některými požadavky

Další věcí, kterou potřebujeme, je FOSRestBundle, abychom mohli zpracovat požadavky REST v naší aplikaci, a znovu byste měli nainstalovat, jak je popsáno v oficiálních dokumentech.

$ skladatel vyžaduje friendsofsymfony / rest-bundle

Pak přidejte balíček do aplikace / AppKernel.php:

třída AppKernel rozšiřuje jádro
{
    public function registerBundles ()
    {
        $ bundles = [
            // ...
            nový FOS \ RestBundle \ FOSRestBundle (),
        ];
        // ...
    }
}

Protože se budeme muset zabývat serializací obsahu a deserializací, budete také muset použít JMSSerializerBundle

Začněte vytvořením balíčku

Další věc, kterou uděláme, je vytvoření balíčku, kde můžeme nastavit náš REST řadič a naše trasy, a řekněme to MySuperRestBundle:

$ php bin / konzole generovat: bundle --namespace = cleverti / MySuperRestBundle - žádná interakce

Nyní je čas na správce

Nyní, když je vytvořen náš balíček, budeme také potřebovat řadič:

$ php bin / konzole generovat: řadič - no-interakce --controller = cleverti \ MySuperRestBundle: Rest

Tím vytvoříte třídu řadičů v src / cleverti / MySuperRestBundle / Controller / RestController.php, na které se právě teď zaměříme.

Příkaz vygenerovat: řadič vygeneruje pouze základní kostru vaší třídy řadičů, což znamená, že budete muset přidat všechny mechaniky, aby váš řadič reagoval na trasy, které chcete definovat, a to se provádí pomocí Akce.

Ve výchozím nastavení vypadá náš řadič takto:

namespace cleverti \ MySuperRestBundle \ Controller;
použijte Symfony \ Bundle \ FrameworkBundle \ Controller \ Controller;
použijte Sensio \ Bundle \ FrameworkExtraBundle \ Configuration \ Route;
třída RestController rozšiřuje Controller
{
}

… Což znamená, že budeme muset provést několik změn. Koneckonců, je to jen počáteční kostra.

Začněme tedy definováním trasy pro náš řadič.

Můžete nastavit předponu pro své řadiče, což je docela užitečné, pokud chcete mít spoustu tras pod stejným řadičem a několik řadičů ve stejném balíčku. Za tímto účelem byste do aplikace / config / routing.yml měli přidat následující položky:

cleverti_my_super_rest_bundle:
    resource: "@ clevertiMySuperRestBundle / Resources / config / routing.yml"
    předpona: / api

V tomto případě bude naše předpona pro náš balíček jednoduše / api. A potom musíme Symfony říct, že náš řadič bude očekávat požadavek REST, takže do src / cleverti / MySuperRestBundle / Resources / config / routing.yml bychom měli přidat následující:

cleverti_my_super_rest_controller:
    typ: odpočinek
    resource: cleverti \ MySuperRestBundle \ Controller \ RestController

Nyní jsme připraveni definovat naše trasy v našem kontroléru. Protože použijeme REST, náš řadič nebude rozšiřovat výchozí řadič Symfony, ale místo toho FOSRestController.

Pojďme tedy definovat testovací akci, abychom viděli kouzlo API v práci, a v tomto případě to nazveme restGetAction. K nastavení naší trasy použijeme také anotaci: @Get („/ get / cleverti“).

Tato trasa bude definovat vaši metodu i cestu k volání. Protože tento řadič je nastaven tak, aby měl předponu / api, budete ji muset volat: GET / api / get / cleverti. Měli bychom něco takového získat v našem kontroléru:

namespace cleverti \ MySuperRestBundle \ Controller;
použijte FOS \ RestBundle \ Controller \ FOSRestController;
použijte Symfony \ Component \ HttpFoundation \ Request;
použijte FOS \ RestBundle \ Controller \ Annotations \ Get;
třída RestController rozšiřuje FOSRestController
{
    / **
     * Tady jde naše cesta
     * @Get ("/ get / cleverti")
     * /
    veřejná funkce restGetAction (požadavek $ $)
    {
        // Udělejte něco s objektem Request
        $ data = array (
            "jméno" => "chytré",
            "extra" => "Je úžasné!"
        );
        $ view = $ this-> view ($ data, 200);
        return $ this-> handleView ($ view);
    }
}

A jsme téměř připraveni.

Potřebujeme pouze nastavit některá základní nastavení, aby FosRestBundle poslouchal volání REST, a protože budeme používat jeho zobrazovací modul, nemusíme pro naše odpovědi nastavovat žádnou šablonu větviček. Do aplikace / config / config.yml tedy přidejte následující položky:

fos_rest:
    routing_loader:
        default_format: json
    Pohled:
        view_response_listener: true

A nyní pokračujte a zavolejte na svou trasu se svým oblíbeným REST klientem, nebo protože metoda definovaná pro tuto trasu je GET, můžete také volat, pokud z webového prohlížeče. V našem příkladu jsme použili Insomnia jako REST klienta.

A úspěch!

Out API poslouchá vaši žádost a odpovědi s odpovědí JSON, přesně tak, jak byla definována. A teď se mě ptáte: A co kontrola přístupu? Nechci, aby většina mých metod API byla veřejně dostupná ...

Nastavení OAuth

Existuje několik způsobů autorizace a jednou z nejpoužívanějších je OAuth 2.0. Umožňuje vám používat ověřování u externího poskytovatele, což je v pohodě, pokud chcete svým uživatelům použít Twitter, Facebook nebo jiného poskytovatele k identifikaci sebe sama. V našem příkladu použijeme jako poskytovatele uživatele FOSUserBundle, takže vytvořme dva balíčky, jeden pro entitu FOSUserBundle a druhý pro entity FOSOAuthServerBundle, které musíme během procesu instalace nastavit:

$ php bin / konzole generovat: bundle --namespace = cleverti / UserBundle - žádná interakce
$ php bin / konzole generovat: bundle --namespace = cleverti / OAuthBundle - žádná interakce

Pak byste měli nainstalovat FOSUserBundle, jak je vysvětleno v oficiální dokumentaci.

A potom nainstalujte FOSOAuthServerBundle, jak je ukázáno zde.

Nezapomeňte také zpřístupnit / api pouze pro ověřené uživatele přidáním do bloku access_control takto:

bezpečnostní:
    ...
    Řízení přístupu:
        - {cesta: ^ / api, role: [IS_AUTHENTICATED_FULLY]}

Sekce cesty bude cesta vašich cest, částečná nebo absolutní, kterou chcete chránit. A část rolí je místo, kde definujete role, které musí mít vaši uživatelé, aby měli přístup k trasám. Protože se jedná pouze o příklad, nebudu se zaměřovat na role, pouze definuji, že přístup může mít jakýkoli ověřený uživatel, ale nikoli anonymní uživatel. O rolích se můžete dozvědět více zde. Takto vypadá moje aplikace / config / config.yml:

dovoz:
    - {zdroj: parametry.yml}
    - {zdroj: security.yml}
    - {zdroj: services.yml}
    - {resource: "@ clevertiMySuperRestBundle / Resources / config / services.yml"}
    - {resource: "@ clevertiOAuthBundle / Resources / config / services.yml"}
    - {resource: "@ clevertiUserBundle / Resources / config / services.yml"}
parametry:
    locale: en
rámec:
    #esi: ~
    #translator: {fallbacks: ['% locale%']}
    secret: '% secret%'
    router:
        resource: '% kernel.project_dir% / app / config / routing.yml'
        strict_requirements: ~
    forma: ~
    csrf_protection: ~
    validace: {enable_annotations: true}
    #serializer: {enable_annotations: true}
    šablonování:
        motory: ['větvička']
    default_locale: '% locale%'
    trusted_hosts: ~
    zasedání:
        # https://symfony.com/doc/current/reference/configuration/framework.html#handler-id
        handler_id: session.handler.native_file
        save_path: '% kernel.project_dir% / var / sessions /% kernel.environment%'
    fragmenty: ~
    http_method_override: true
    aktiva: ~
    php_errors:
        log: true
# Konfigurace větvičky
větvička:
    ladění: '% kernel.debug%'
    strict_variables: '% kernel.debug%'
# Konfigurace doktríny
doktrína:
    dbal:
        driver: pdo_mysql
        hostitel: '% database_host%'
        port: '% database_port%'
        dbname: '% database_name%'
        user: '% database_user%'
        heslo: '% database_password%'
        charset: UTF8
    orm:
        auto_generate_proxy_classes: '% kernel.debug%'
        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true
# Konfigurace služby Swiftmailer
swiftmailer:
    transport: '% mailer_transport%'
    hostitel: '% mailer_host%'
    uživatelské jméno: '% mailer_user%'
    heslo: '% mailer_password%'
    cívka: {typ: paměť}
# FosRestBundle Configuration
fos_rest:
    routing_loader:
        default_format: json
    Pohled:
        view_response_listener: true
# FOSUserBundle Configuration
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: cleverti \ UserBundle \ Entity \ User
    from_email:
        adresa: "% mailer_user%"
        sender_name: "% mailer_user%"
# FOSOAuthServerBundle Configuration
fos_oauth_server:
    db_driver: orm
    client_class: cleverti \ OAuthBundle \ Entity \ Client
    access_token_class: cleverti \ OAuthBundle \ Entity \ AccessToken
    refresh_token_class: cleverti \ OAuthBundle \ Entity \ RefreshToken
    auth_code_class: cleverti \ OAuthBundle \ Entity \ AuthCode
    služba:
        user_provider: fos_user.user_provider.username
        možnosti:
            access_token_lifetime: 86400
            aktualizace_token_lifetime: 1209600
            auth_code_lifetime: 30

A toto je moje aplikace / config / security.yml:

bezpečnostní:
    kodéry:
        FOS \ UserBundle \ Model \ UserInterface: bcrypt
        
    role_hierarchie:
        ROLE_ADMIN: ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN
    poskytovatelé:
        v paměti:
            paměť: ~
        fos_userbundle:
            id: fos_user.user_provider.username
            
    firewally:
        dev:
            vzor: ^ / (_ (profiler | wdt) | css | obrázky | js) /
            bezpečnost: nepravdivá
            
        oauth_token:
            vzor: ^ / oauth / v2 / token
            bezpečnost: nepravdivá
        oauth_autorizovat:
            vzor: ^ / oauth / v2 / auth
            form_login:
                poskytovatel: fos_userbundle
                check_path: / oauth / v2 / auth_login_check
                login_path: / oauth / v2 / auth_login
                use_referer: true
        api:
            vzor: ^ / api
            fos_oauth: true
            bez státní příslušnosti: pravda
            anonymní: nepravdivé
        hlavní:
            vzor: ^ /
            form_login:
                poskytovatel: fos_userbundle
                csrf_token_generator: security.csrf.token_manager
            
            anonymní: pravda
    Řízení přístupu:
        - {cesta: ^ / api, role: [IS_AUTHENTICATED_FULLY]}

Od této chvíle, pokud se pokusíte na vaší trase uskutečnit další hovor, neměli byste již mít přístup, protože ještě nejste ověřeni. To je to, co byste měli získat:

Až skončíme s instalací FOSUserBundle a FOSOAuthServerBundle, musíme vytvořit klienta OAuth a uživatele, abychom mohli vytvořit přístupové tokeny Oauth. Potřebujeme to pro naši žádost.

Takže vytvořte následující příkaz na src / cleverti / OAuthBundle / Command / ClientCreateCommand.php:

 setName ('oauth: client: create')
            -> setDescription ('Vytvoření nového klienta')
            -> addOption ('redirect-uri', null, InputOption :: VALUE_REQUIRED | InputOption :: VALUE_IS_ARRAY, 'Nastaví přesměrování uri. Použijte vícekrát k nastavení více uris.', null)
            -> addOption ('grant-type', null, InputOption :: VALUE_REQUIRED | InputOption :: VALUE_IS_ARRAY, 'Nastavit povolený typ grantu. Použít vícekrát k nastavení více typů grantů', null)
        ;
    }
    spuštění chráněné funkce (InputInterface $ input, OutputInterface $ output)
    {
        $ clientManager = $ this-> getContainer () -> get ('fos_oauth_server.client_manager.default');
        $ client = $ clientManager-> createClient ();
        $ client-> setRedirectUris ($ input-> getOption ('redirect-uri'));
        $ client-> setAllowedGrantTypes ($ input-> getOption ('grant-type'));
        $ clientManager-> updateClient ($ client);
        $ output-> writeln ("Přidán nový klient s veřejným id ". $ client-> getPublicId (). " a tajný ". $ client-> getSecret (). " ");
    }
}

A vytvořte klienta OAuth spuštěním příkazu, který jste právě vytvořili, se dvěma možnostmi - URI přesměrování, které chcete použít, a typy grantů, které chcete tomuto klientovi povolit, což by vám mělo poskytnout výstup, jako je následující:

$ php bin / console oauth: client: create --redirect-uri = http: //www.cleverti.com --grant-type = heslo --grant-type = refresh_token
Přidán nový klient s veřejným id.

K vytvoření uživatele můžete použít výchozí příkaz poskytovaný FOSUserBundle:

$ php bin / console fos: user: create ricardo.correia ricardo.correia@cleverti.com heslo

Nyní se pokusíme požádat o přístupový token tím, že pošlete požadavek POST na cestu OAuth / oauth / v2 / token. Tato trasa očekává grant_typ s hodnotovým heslem, client_id a client_secret a uživatelským jménem a heslem uživatele:

{
    "grant_type": "heslo",
    "client_id": "1_4654cpi0zu0wokk4g04gk0w444wkkwcs4sg0okoo0gks0gcokg",
    "client_secret": "1h7xdjjo6ixwwow0w00oss4sgc0w8o48ocgw808w0gg4s40owc",
    "username": "ricardo.correia",
    "heslo": "heslo"
}

Pokud požádáme našeho REST klienta, dostaneme následující výsledek:

Dobře, všechno zatím jde skvěle a máme přístupový token.

Nyní budeme muset použít náš přístupový token ve všech našich požadavcích nastavením záhlaví Autorizace následujícím způsobem:

Autorizace: Nosič MTI4ZmUyNTg1ZDgwM2Y0ZmJlZjg3OWNlNTA2ZDE5ZTk4ZTQzZGMzNjllOGE4YWI5Yzc0ZWQxMWQ1MjVjNmY5MA

Zkusme se tedy dostat na naši chráněnou trasu / api / get / cleverti, ale tentokrát pomocí záhlaví Autorizace:

Úspěch! Naše trasa je chráněna a dostupná pouze pro ověřené uživatele pomocí protokolu OAuth 2.0. A je to skoro všechno.

Možná budete chtít aktualizovat přístupový token po vypršení jeho životnosti, a to také pomocí trasy / oauth / v2 / token, ale tentokrát budete muset poslat grant_typ jako refresh_token, client_id a client_secret a Obnovit místo uživatele a hesla:

{
    "grant_type": "refresh_token",
    "client_id": "1_4654cpi0zu0wokk4g04gk0w444wkkwcs4sg0okoo0gks0gcokg",
    "client_secret": "1h7xdjjo6ixwwow0w00oss4sgc0w8o48ocgw808w0gg4s40owc",
    "refresh_token": "OWQyNTI1MDk4OTlhZTk0NDdlYzA0YTM4ZTFlZDZjMmQxNDY3OTBjMzBiNTYzOWMwMTgxN2UwMGYwYWE3M2RiYw"
}

A toto volání vrátí nový access_token a nový refresh_token:

A to je prozatím vše. Je dobré jít! Pokud potřebujete, můžete se také podívat na demo projekt Git zde.

Doufáme, že si užijete své kódování stejně jako my. Bavte se!

Napsal Ricardo Correia | Developer ve společnosti Cleverti

Tento článek byl původně zveřejněn na Clevertiho blogu