API Rest con Symfony 4
Habilitar controladores API Rest
Instalar los siguientes paquetes:
jms/serializer-bundle friendsofsymfony/rest-bundle sensio/framework-extra-bundle
Configurar el archivo config/packages/fos_rest.yaml
fos_rest: routing_loader: default_format: json include_format: false body_listener: true format_listener: rules: - { path: '^/', priorities: ['json'], fallback_format: json, prefer_extension: false } param_fetcher_listener: true access_denied_listener: json: true view: view_response_listener: 'force' formats: json: true
Agregar estas lineas al archivo config/services.yaml
sensio_framework_extra.view.listener: alias: Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener
Controllador API Rest
<?php namespace App\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use FOS\RestBundle\Controller\AbstractFOSRestController; use FOS\RestBundle\Controller\Annotations as Rest; use Symfony\Component\Routing\Annotation\Route; /** * @Route("/") */ class IndexController extends AbstractFOSRestController { /** * @Rest\Get("/") */ public function index() { return [ "service" => "Mi API Rest", "version" => "0.0.0", "status" => "ok" ]; } }
Habilitar CORS
Instalar el siguiente paquete
composer req cors
Configurar config/packages/nelmio_cors.yaml
nelmio_cors: defaults: origin_regex: true allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] allow_headers: ['Content-Type', 'Authorization'] expose_headers: ['Link'] max_age: 3600 paths: '^/': ~
Habilitar autenticación JWT
Instalar el siguiente paquete
composer req "lexik/jwt-authentication-bundle"
Generar claves SSH
mkdir config/jwt openssl genrsa -out config/jwt/private.pem -aes256 4096 openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem
La contraseña asignada a la clave privada debe guardarse en la variable JWT_PASSPHRASE en el archivo .env.local
Agregar ruta de controlador de autenticación en config/routes.yml
login_check: path: /login_check methods: [POST]
Configurar seguridad en config/packages/security.yaml
security: encoders: App\Entity\User: algorithm: bcrypt providers: entity_provider: entity: class: App\Entity\User property: username firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false login: pattern: ^/login stateless: true anonymous: true json_login: check_path: /login_check success_handler: lexik_jwt_authentication.handler.authentication_success failure_handler: lexik_jwt_authentication.handler.authentication_failure api: pattern: ^/api stateless: true anonymous: false provider: entity_provider guard: authenticators: - lexik_jwt_authentication.jwt_token_authenticator main: anonymous: true access_control: - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Con esta configuración todas las rutas que empiecen por /api quedarán protegidas por la autenticación.
Crear un usuario
Los usuarios pueden crearse instanciando y persistiendo un objeto User. Para encriptar la contraseña se debe inyectar la interface UserPasswordEncoderInterface. Este es un ejemplo de un método de registro dentro de un controlador:
/** * @Rest\Post("/register") */ public function register(Request $request, UserPasswordEncoderInterface $encoder) { $em = $this->getDoctrine()->getManager(); $username = $request->request->get('_username'); $password = $request->request->get('_password'); $user = new User($username); $user->setPassword($encoder->encodePassword($user, $password)); $em->persist($user); $em->flush(); return [ "response" => sprintf('User %s successfully created', $user->getUsername()) ]; }
También se puede insertar un usuario en la base de datos directamente. Pero antes se debe generar una contraseña encriptada con el comando:
bin/console security:encode-password 'your_plain_password'
Autenticación con Postman
Obtener token
Enviar petición con token
Se debe agregar el header "Authorization" en la petición con el prefijo "Bearer" seguido de un espacio y el token obtenido en el paso anterior.
hola estaba siguiendo la guia y me tira el siguiente error al intentar ingresar a la ruta principal declarada en el controller
Invalid type for path «fos_rest.routing_loader». Expected boolean, but got array.
desde ya muchas gracias
gran ayuda ! gracias