Symfony backend rest api, oauth authentikáció
Symfony (3.4) rest api-t építünk a következő leírásban. A leírás az oldal alján levő linkekből készült. Friendofsymfony csomagot használjuk rest szervernek, szükség van az oauth-server csomagra is az authentikációhoz és a serializer csomagra is. JSON Web Token (JWT) OAuth 2.0 Bearer token-es authentikációt használunk. Csak érvényes access_token-en rendelkező hívást fogadunk el. Ha lejár akkor kérni kell újat a refresh_token küldésével. |
|
Írjuk be a composer.json-be a következő csomagokat és nyomjunk composer update-et:
"beberlei/DoctrineExtensions": "^1.1", "doctrine/doctrine-bundle": "^1.6", "doctrine/orm": "^2.5", "friendsofsymfony/oauth-server-bundle": "1.6.1", "friendsofsymfony/rest-bundle": "2.3.*", "friendsofsymfony/user-bundle": "2.1.2", "incenteev/composer-parameter-handler": "^2.0", "jms/serializer-bundle": "2.3.*", A config.yml-be a következőket állítsuk be: fos_rest: routing_loader: default_format: json include_format: false body_listener: true format_listener: rules: prefer_extension: false fallback_format: json param_fetcher_listener: true access_denied_listener: json: true # Enable serializer for the REST API serializer: serialize_null: true view: view_response_listener: 'force' formats: json: true # Disable CSRF protection disable_csrf_role: ROLE_API fos_user: db_driver: orm firewall_name: api # Seems to be used when registering user/reseting password, # but since there is no "login", as so it seems to be useless in # our particular context, but still required by "FOSUserBundle" user_class: UsersBundle\Entity\User from_email: address: "test@app.eu" sender_name: "Test App" fos_oauth_server: db_driver: orm client_class: UsersBundle\Entity\Client access_token_class: UsersBundle\Entity\AccessToken refresh_token_class: UsersBundle\Entity\RefreshToken auth_code_class: UsersBundle\Entity\AuthCode service: user_provider: fos_user.user_provider.username_email # This property will be used when valid credentials are given to load the user upon access token creation jms_serializer: metadata: auto_detection: true handlers: datetime: default_format: c security.yml: security: encoders: FOS\UserBundle\Model\UserInterface: algorithm: bcrypt cost: 15 providers: fos_userbundle: id: fos_user.user_provider.username # fos_user.user_provider.username_email does not seem to work (OAuth-spec related ("username + password") ?) firewalls: oauth_token: # Everyone can access the access token URL. pattern: ^/oauth/v2/token security: false api: pattern: ^/ # All URLs are protected fos_oauth: true # OAuth2 protected resource stateless: true # Do no set session cookies anonymous: false # Anonymous access is not allowed routing.yml # app/config/routing.yml web_test: resource: "@TestBundle/Controller/Rest/TestController.php" type: annotation NelmioApiDocBundle: resource: "@NelmioApiDocBundle/Resources/config/routing.yml" prefix: /api/doc fos_oauth_server_token: resource: "@UsersBundle/Resources/config/routing/token.xml" Itt ezen a ponton kövessük a következő linken a User Entity részt az entity-k és táblák létrehozásához: User Entity Generáljunk client_id-t (itt a kliens a frontend-et jelenti, nem userenként kell generálni) a következő paranccsal: # php bin/console fos:oauth-server:create-client --grant-type="password" --grant-type="refresh_token" Amit generáltunk client_id és secret_id-t írjuk be a következő insert sql-be: INSERT INTO `oauth2_clients` (`id`, `random_id`, `redirect_uris`, `secret`, `allowed_grant_types`) VALUES (1, '3bcbxd9e24g0gk4swg0kwgcwg4o8k8g4g888kwc44gcc0gwwk4', 'a:0:{}', '4ok2x70rlfokc8g0wws8c8kwcokw80k44sg48goc0ok4w0so0k', 'a:2:{i:0;s:8:\"password\";i:1;s:13:\"refresh_token\";}'); Készítsünk controllert a rest api hívásoknak (példa egy random controllerből): <?php namespace TestBundle\Controller\Rest; use ScheduleBundle\Entity\Test; use FOS\RestBundle\Controller\FOSRestController; use Symfony\Component\HttpFoundation\Request; use FOS\RestBundle\Controller\Annotations as Rest; use Symfony\Component\HttpFoundation\Response; use FOS\RestBundle\View\View; use FOS\RestBundle\Controller\Annotations\Route; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; /** * */ class TestController extends FOSRestController { /** * @Rest\Get("/api/test") * * * @return Response */ public function getAction() { $em = $this->getDoctrine()->getManager(); $restresult = $em->getRepository('TestBundle:Test')->getAllTest(); if ($restresult === null) { return new View("there are no test exist", Response::HTTP_NOT_FOUND); } $encoders = array(new XmlEncoder(), new JsonEncoder()); $normalizers = array(new ObjectNormalizer()); $serializer = new Serializer($normalizers, $encoders); $jsonContent = $serializer->serialize($restresult, 'json'); return new Response($jsonContent); } /** * @Rest\Get("/api/test/show/{id}") */ public function idAction($id) { $singleresult = $this->getDoctrine()->getRepository('TestBundle:Test')->find($id); if ($singleresult === null) { return new View("test not found", Response::HTTP_NOT_FOUND); } $encoders = array(new XmlEncoder(), new JsonEncoder()); $normalizers = array(new ObjectNormalizer()); $serializer = new Serializer($normalizers, $encoders); $jsonContent = $serializer->serialize($singleresult, 'json'); return new Response($jsonContent); } /** * @Rest\Post("/api/test/add") * */ public function addAction(Request $request) { $data = $request->get('data'); $data = json_decode(base64_decode($data), true); $test = new Test(); $testNumber = isset($data['testNumber']) ? $data['testNumber'] : NULL; $timeFrom = isset($data['timeFrom']) ? $data['timeFrom'] : NULL; $timeTo = isset($data['timeTo']) ? $data['timeTo'] : NULL; $name = isset($data['name']) ? $data['name'] : NULL; $color = isset($data['color']) ? $data['color'] : NULL; if (empty($testNumber)) { return new View("NULL VALUES ARE NOT ALLOWED" . '-' . $request, Response::HTTP_NOT_ACCEPTABLE); } $test->setTestNumber($testNumber); $test->setTimeFrom($timeFrom); $test->setTimeTo($timeTo); $test->setName($name); $test->setColor($color); $em = $this->getDoctrine()->getManager(); $em->persist($test); $em->flush(); return new View("Test Added Successfully", Response::HTTP_OK); } /** * @Rest\Put("/api/test/edit/{id}") */ public function updateAction($id, Request $request) { $data = $request->get('data'); $sn = $this->getDoctrine()->getManager(); $test = $this->getDoctrine()->getRepository('TestBundle:Test')->find($id); $data = json_decode(base64_decode($data), true); $testNumber = isset($data['testNumber']) ? $data['testNumber'] : NULL; $timeFrom = isset($data['timeFrom']) ? $data['timeFrom'] : NULL; $timeTo = isset($data['timeTo']) ? $data['timeTo'] : NULL; $name = isset($data['name']) ? $data['name'] : NULL; $color = isset($data['color']) ? $data['color'] : NULL; if (empty($test)) { $response = new Response("test not found"); $response->headers->set('Content-Type', 'application/json'); $response->headers->set('Access-Control-Allow-Origin', 'http://192.168.1.2:4200'); $response->headers->set('Access-Control-Allow-Methods', 'PUT'); return new View("test not found", Response::HTTP_NOT_FOUND); } elseif (!empty($data['testNumber']) && !empty($data['timeFrom']) && !empty($data['timeTo'])) { $test->setTestNumber($testNumber); $test->setTimeFrom($timeFrom); $test->setTimeTo($timeTo); $test->setName($name); $test->setColor($color); $sn->persist($test); $sn->flush(); $response = new Response("Test Updated Successfully"); $response->headers->set('Content-Type', 'application/json'); $response->headers->set('Access-Control-Allow-Origin', 'http://192.168.1.2:4200'); $response->headers->set('Access-Control-Allow-Methods', 'PUT'); return new View("Test Updated Successfully", Response::HTTP_OK); } else { $response = new Response("Test name or value cannot be empty"); $response->headers->set('Content-Type', 'application/json'); $response->headers->set('Access-Control-Allow-Origin', 'http://192.168.1.2:4200'); $response->headers->set('Access-Control-Allow-Methods', 'PUT'); return new View("Test name or active field cannot be empty", Response::HTTP_NOT_ACCEPTABLE); } } /** * @Rest\Delete("/api/test/delete/{id}") */ public function deleteAction($id) { $sn = $this->getDoctrine()->getManager(); $test = $this->getDoctrine()->getRepository('TestBundle:Test')->find($id); if (empty($test)) { return new View("test not found", Response::HTTP_NOT_FOUND); } else { $sn->remove($test); $sn->flush(); } return new View("deleted successfully", Response::HTTP_OK); } } Két beállításra még szükségünk lesz. /src/UsersBundle/Resources/config/routing/authorize.xml: <?xml version="1.0" encoding="UTF-8" ?> <routes xmlns="http://symfony.com/schema/routing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> <route id="fos_oauth_server_authorize" path="/oauth/v2/auth" methods="GET POST OPTIONS"> <default key="_controller">fos_oauth_server.controller.authorize:authorizeAction</default> </route> </routes> /src/UsersBundle/Resources/config/routing/token.xml: <?xml version="1.0" encoding="UTF-8" ?> <routes xmlns="http://symfony.com/schema/routing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> <route id="fos_oauth_server_token" path="/oauth/v2/token" methods="GET POST OPTIONS"> <default key="_controller">fos_oauth_server.controller.token:tokenAction</default> </route> </routes> Indítsuk el a rest apit: # php bin/console server:start 0.0.0.0:8000 Ha szükséges üríthetjük a cache-t: # php app/console fos:oauth-server:clean Bejelentkezés példa: POST http://localhost:8000/app_dev.php/oauth/v2/token grant_type=password client_id=1_3bcbxd9e24g0gk4swg0kwgcwg4o8k8g4g888kwc44gcc0gwwk4 client_secret=4ok2x70rlfokc8g0wws8c8kwcokw80k44sg48goc0ok4w0so0k username=admin password=admin Új token kérés példa: POST http://localhost:8000/app_dev.php/oauth/v2/token grant_type=refresh_token client_id=1_3bcbxd9e24g0gk4swg0kwgcwg4o8k8g4g888kwc44gcc0gwwk4 client_secret=4ok2x70rlfokc8g0wws8c8kwcokw80k44sg48goc0ok4w0so0k refresh_token=ZDg4NDg4NjI1MjBlOWQ3MjYyN2Q0MTlkNTc5ZjNkNWI0MmIxYWUwMTg1YmQ5OTAyNDI0ZjRkYjU2NWViYjhlNg Egy rest api get hívás példa: GET http://localhost:8000/app_dev.php/api/test Authorization=Bearer ZDg4NDg4NjI1MjBlOWQ3MjYyN2Q0MTlkNTc5ZjNkNWI0MmIxYWUwMTg1YmQ5OTAyNDI0ZjRkYjU2NWViYjhlNg Olvasnivaló: https://gist.github.com/diegonobre/341eb7b793fc841c0bba3f2b865b8d66 https://stackoverflow.com/questions/46362122/custom-authentication-in-a-symfony-3-using-external-rest-api https://www.cloudways.com/blog/rest-api-in-symfony-3-1/ https://phpbuilder.com/creating-a-rest-api-in-symfony-3/ https://codereviewvideos.com/course/symfony-3-rest-tutorial https://www.cloudways.com/blog/rest-api-in-symfony-3-1/ https://stackoverflow.com/questions/40386573/symfony-fos-oauth-with-custom-user |
2018.11.17. |
Figyelem! Az itt olvasható leírások, nem teljesek és nem biztos, hogy pontosak. Nem
frissülnek folyamatosan, ezért nem mindegyik használható az aktuális verziójú rendszerekben. Mindenki saját
felelősségére használja az itt található ötleteket. Az esetleges károkért nem vállalunk felelősséget.