gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Establecer una variable global está bien. Pero si utilizas Stimulus, hay una forma mejor. Podemos pasar datos del servidor como un valor a un controlador Stimulus.
Por supuesto, esto es una aplicación Vue. Pero si miras en templates/main/homepage.html.twig
, estamos utilizando el paquete symfony/ux-vue
para renderizar esto:
... lines 1 - 2 | |
{% block body %} | |
<div {{ vue_component('TreasureConnectApp', { | |
entrypoint: path('api_entrypoint') | |
}) }}></div> | |
{% endblock %} |
Entre bastidores, eso activa un pequeño controlador Stimulus que inicia y renderiza el componente Vue. Cualquier argumento que pasemos aquí se envía al controlador Stimulus como un valor... y luego se reenvía como props a la aplicación Vue. Lo que vamos a hacer es "más o menos" específico de Vue, pero podrías utilizar esta estrategia para pasar valores a cualquier controlador Stimulus.
Primero, en el componente Vue, vamos a permitir que se pase una nueva prop llamada user
:
... lines 1 - 26 | |
<script setup> | |
... lines 28 - 32 | |
const props = defineProps(['entrypoint', 'user']) | |
... lines 34 - 40 | |
</script> |
Si no utilizas Vue, no te preocupes demasiado por los detalles. Para asegurarnos de que llega aquí console.log(props.user)
. E inicializa los datos enprops.user
:
... lines 1 - 26 | |
<script setup> | |
... lines 28 - 32 | |
const props = defineProps(['entrypoint', 'user']) | |
console.log(props.user); | |
const user = ref(props.user); | |
... lines 36 - 40 | |
</script> |
A continuación, en base.html.twig
, quita todo eso de window.user
:
<html> | |
... lines 3 - 15 | |
<body> | |
{% block body %}{% endblock %} | |
</body> | |
</html> |
Y en homepage.html.twig
, pasa un nuevo user
prop set a app.user
:
... lines 1 - 2 | |
{% block body %} | |
<div {{ vue_component('TreasureConnectApp', { | |
entrypoint: path('api_entrypoint'), | |
user: app.user | |
}) }}></div> | |
{% endblock %} |
Ahora, si te mueves y actualizas, ¿no funciona? Parece que estamos autenticados como... ¿nada?
Si escarbas un poco, verás que estamos enviando el user
a Stimulus como {}
vacío. ¿Por qué? Porque cuando envías datos a Stimulus, éste no utiliza el serializador para transformarlos en JSON: sólo utiliza json_encode()
. Y eso no es suficiente.
Así que tenemos que serializarlo nosotros mismos. Para ello, abresrc/Controller/MainController.php
. Aquí está el controlador que renderiza esa plantilla. Autoconecta un servicio llamado NormalizerInterface
y luego pasa una variable a nuestra plantilla llamada userData
ajustada a $normalizer->normalize()
. Oh, ¡pero necesitamos al usuario! Añade otro argumento al controlador con el nuevo atributo#[CurrentUser]
, type-hint User
, digamos $user
, y luego = null
en caso de que no estemos autenticados. Más abajo, la normalización convertirá el objeto en una matriz. Así que pasa $user
y luego el formato de la matriz, que es jsonld
: queremos todos los campos JSON-LD. Por último, pasa el contexto de serialización con'groups' => 'user:read'
:
... lines 1 - 4 | |
use App\Entity\User; | |
... lines 6 - 8 | |
use Symfony\Component\Security\Http\Attribute\CurrentUser; | |
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; | |
class MainController extends AbstractController | |
{ | |
'/') ( | |
public function homepage(NormalizerInterface $normalizer, #[CurrentUser] User $user = null): Response | |
{ | |
return $this->render('main/homepage.html.twig', [ | |
'userData' => $normalizer->normalize($user, 'jsonld', [ | |
'groups' => ['user:read'], | |
]), | |
]); | |
} | |
} |
¡Último paso! En la plantilla, establece la propiedad user
en userData
:
... lines 1 - 2 | |
{% block body %} | |
<div {{ vue_component('TreasureConnectApp', { | |
... line 5 | |
user: userData, | |
}) }}></div> | |
{% endblock %} |
Ya que el sistema Stimulus ejecutará ese array a través de json_encode()
que transformará ese array en JSON. Cuando pasemos y refresquemos .... ¡ya lo tienes! Puedes ver que todo el JSON se pasa al controlador Stimulus... y luego se pasa a Vue como prop.
Vuelve a girar y asegúrate de sacar ese console.log()
de ahí:
... lines 1 - 26 | |
<script setup> | |
... lines 28 - 33 | |
console.log(props.user); | |
... lines 35 - 40 | |
</script> |
Todavía no lo hemos visto, pero cuando empecemos a hacer peticiones a nuestra API, esas peticiones se autenticarán gracias a la sesión. Cuando utilices sesiones con tu API, puede que leas que necesitas protección CSRF. ¿Necesitamos tokens CSRF?
La respuesta rápida es: probablemente no. Mientras utilices algo llamado cookies SameSite - que son automáticas en Symfony - entonces tu API probablemente no necesite preocuparse por la protección CSRF. Pero ten en cuenta dos cosas. En primer lugar, asegúrate de que tus peticiones GET no tienen efectos secundarios. No hagas una tontería como permitir que el cliente de la API haga una petición GET... pero luego guardes algo en la base de datos. En segundo lugar, algunos navegadores antiguos -como IE 11- no admiten las cookies SameSite. Así que al renunciar a los tokens CSRF, podrías estar permitiendo que un pequeño porcentaje de tus usuarios sean susceptibles de sufrir ataques CSRF.
Si quieres saber más, nuestro tutorial sobre la API Platform 2 tiene un capítulo entero sobre Cookies SameSite y tokens CSRF.
A continuación, pasemos al otro caso de uso de la autenticación: Los tokens API.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^3.0", // v3.1.2
"doctrine/annotations": "^2.0", // 2.0.1
"doctrine/doctrine-bundle": "^2.8", // 2.8.3
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
"doctrine/orm": "^2.14", // 2.14.1
"nelmio/cors-bundle": "^2.2", // 2.2.0
"nesbot/carbon": "^2.64", // 2.66.0
"phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
"phpstan/phpdoc-parser": "^1.15", // 1.16.1
"symfony/asset": "6.2.*", // v6.2.5
"symfony/console": "6.2.*", // v6.2.5
"symfony/dotenv": "6.2.*", // v6.2.5
"symfony/expression-language": "6.2.*", // v6.2.5
"symfony/flex": "^2", // v2.2.4
"symfony/framework-bundle": "6.2.*", // v6.2.5
"symfony/property-access": "6.2.*", // v6.2.5
"symfony/property-info": "6.2.*", // v6.2.5
"symfony/runtime": "6.2.*", // v6.2.5
"symfony/security-bundle": "6.2.*", // v6.2.6
"symfony/serializer": "6.2.*", // v6.2.5
"symfony/twig-bundle": "6.2.*", // v6.2.5
"symfony/ux-react": "^2.6", // v2.7.1
"symfony/ux-vue": "^2.7", // v2.7.1
"symfony/validator": "6.2.*", // v6.2.5
"symfony/webpack-encore-bundle": "^1.16", // v1.16.1
"symfony/yaml": "6.2.*" // v6.2.5
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
"mtdowling/jmespath.php": "^2.6", // 2.6.1
"phpunit/phpunit": "^9.5", // 9.6.3
"symfony/browser-kit": "6.2.*", // v6.2.5
"symfony/css-selector": "6.2.*", // v6.2.5
"symfony/debug-bundle": "6.2.*", // v6.2.5
"symfony/maker-bundle": "^1.48", // v1.48.0
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/phpunit-bridge": "^6.2", // v6.2.5
"symfony/stopwatch": "6.2.*", // v6.2.5
"symfony/web-profiler-bundle": "6.2.*", // v6.2.5
"zenstruck/browser": "^1.2", // v1.2.0
"zenstruck/foundry": "^1.26" // v1.28.0
}
}