Autenticación en Vue.js

 

 

 

  • Implemente rápidamente. Implementar inteligentemente
  • Patrones de diseño para interfaces de IA, con Vitaly Friedman

  • Índice
    1. Dependencias #
  • La API de back- end
  • Proyecto de andamio #
  • 1. Configuración de Vuex con Axios #
    1. Módulos #
  • Configuración de componentes #
    1. 1. NavBar.vueY App.vuecomponentes #
    2. 2. Componentes de vistas #
  • 2. Definición de rutas
  • 3. Manejo de usuarios
  • 4.Manejo de tokens vencidos (solicitudes prohibidas) #
  • Conclusión #
  • Cada aplicación web que maneja datos específicos del usuario necesita implementar autenticación. Saber cómo hacer esto es importante para los desarrolladores de Vue, y eso es lo que este artículo pretende resaltar. Este tutorial resultará útil para los desarrolladores principiantes que quieran aprender sobre la autenticación en Vue. Para poder seguirlo, necesitará tener un buen conocimiento de Vue y Vuex.

     

    La autenticación es una característica muy necesaria para las aplicaciones que almacenan datos de usuario. Es un proceso de verificación de la identidad de los usuarios, garantizando que los usuarios no autorizados no puedan acceder a datos privados (datos que pertenecen a otros usuarios). Esto lleva a tener rutas restringidas a las que solo pueden acceder usuarios autenticados. Estos usuarios autenticados se verifican utilizando sus datos de inicio de sesión (es decir, nombre de usuario/correo electrónico y contraseña) y asignándoles un token que se utilizará para acceder a los recursos protegidos de una aplicación.

    En este artículo, aprenderá sobre:

    1. Configuración de Vuex con Axios
    2. Definición de rutas
    3. Manejo de usuarios
    4. Manejo de token vencido

    Dependencias #

    Trabajaremos con las siguientes dependencias que ayudan en la autenticación:

     

    • Axios
      Para enviar y recuperar datos de nuestra API
    • Vuex
      Para almacenar datos obtenidos de nuestra API
    • Vue-Router
      Para navegación y protección de Rutas

    Trabajaremos con estas herramientas y veremos cómo pueden funcionar juntas para proporcionar una funcionalidad de autenticación sólida para nuestra aplicación.

    La API de back- end

    Construiremos un sitio de blog simple, que utilizará esta API . Puede consultar los documentos para ver los puntos finales y cómo se deben enviar las solicitudes.

    En los documentos, notará que algunos puntos finales están conectados con un candado. Esta es una forma de mostrar que solo los usuarios autorizados pueden enviar solicitudes a esos puntos finales. Los puntos finales sin restricciones son los puntos finales /registery /login. Se debe devolver un error con el código de estado 401cuando un usuario no autenticado intenta acceder a un punto final restringido.

    Después de que un usuario inicie sesión exitosamente, el token de acceso junto con algunos datos se recibirá en la aplicación Vue, que se usará para configurar la cookie y se adjuntará en el encabezado de la solicitud para usarse en futuras solicitudes. El backend verificará el encabezado de la solicitud cada vez que se realice una solicitud a un punto final restringido. No caiga en la tentación de almacenar el token de acceso en el almacenamiento local.

    ( Vista previa grande )

    Proyecto de andamio #

    Usando Vue CLI, ejecute el siguiente comando para generar la aplicación:

    vue create auth-project

    Navegue a su nueva carpeta:

    cd auth-project

    Agregue vue-router e instale más dependencias: vuex y axios:

    vue add router npm install vuex axios

    Ahora ejecuta tu proyecto y deberías ver lo que tengo a continuación en tu navegador:

    npm run serve

    1. Configuración de Vuex con Axios #

    Axios es una biblioteca de JavaScript que se utiliza para enviar solicitudes desde el navegador a las API. Según la documentación de Vuex ;

    “Vuex es una biblioteca y patrón de gestión de estado para aplicaciones Vue.js. Sirve como un almacén centralizado para todos los componentes de una aplicación, con reglas que garantizan que el estado sólo pueda modificarse de forma predecible”.

    ¿Qué significa eso? Vuex es una tienda utilizada en una aplicación Vue que nos permite guardar datos que estarán disponibles para cada componente y proporciona formas de cambiar dichos datos. Usaremos Axios en Vuex para enviar nuestras solicitudes y realizar cambios en nuestro estado (datos). Axios se utilizará en Vuex actionspara enviar GETy POSTla respuesta obtenida se utilizará para enviar información mutationsy actualizar los datos de nuestra tienda.

     

    Para solucionar el restablecimiento de Vuex después de la actualización, trabajaremos con vuex-persistedstate, una biblioteca que guarda nuestros datos de Vuex entre recargas de página.

    npm install --save vuex-persistedstate

    Ahora creemos una nueva carpeta storeen src, para configurar la tienda Vuex. En la storecarpeta, cree una nueva carpeta; modulesy un archivo index.js. Es importante tener en cuenta que solo necesita hacer esto si la carpeta no se crea automáticamente.

    import Vuex from 'vuex';import Vue from 'vue';import createPersistedState from "vuex-persistedstate";import auth from './modules/auth';// Load VuexVue.use(Vuex);// Create storeexport default new Vuex.Store({ modules: { auth }, plugins: [createPersistedState()]});

    Aquí estamos haciendo uso Vuexe importando una autenticación modulede la modulescarpeta a nuestra tienda.

    Módulos #

    Los módulos son diferentes segmentos de nuestra tienda que manejan tareas similares juntas, que incluyen:

    • estado
    • comportamiento
    • mutaciones
    • captadores

    Antes de continuar, editemos nuestro main.jsarchivo.

    import Vue from 'vue'import App from './App.vue'import router from './router';import store from './store';import axios from 'axios';axios.defaults.withCredentials = trueaxios.defaults.baseURL = 'https://gabbyblog.herokuapp.com/';Vue.config.productionTip = falsenew Vue({ store, router, render: h = h(App)}).$mount('#app')

    Importamos el storeobjeto de la ./storecarpeta así como el paquete Axios.

    Como se mencionó anteriormente, la cookie del token de acceso y otros datos necesarios obtenidos de la API deben configurarse en los encabezados de solicitud para futuras solicitudes. Dado que utilizaremos Axios al realizar solicitudes, debemos configurar Axios para utilizarlo. En el fragmento anterior, lo hacemos usando axios.defaults.withCredentials = true, esto es necesario porque, de forma predeterminada, Axios no pasa cookies.

    aaxios.defaults.withCredentials = truees una instrucción para Axios para enviar todas las solicitudes con credenciales como; encabezados de autorización, certificados de cliente TLS o cookies (como en nuestro caso).

    Configuramos nuestra axios.defaults.baseURLsolicitud de Axios en nuestro. APIDe esta manera, cada vez que enviamos a través de Axios, utiliza esta URL base. Con eso, podemos agregar solo nuestros puntos finales como /registery /logina nuestras acciones sin indicar la URL completa cada vez.

    Ahora dentro de la modulescarpeta storecrea un archivo llamadoauth.js

    //store/modules/auth.jsimport axios from 'axios';const state = {};const getters = {};const actions = {};const mutations = {};export default { state, getters, actions, mutations};

    state#

    En nuestro statedict, vamos a definir nuestros datos y sus valores predeterminados:

     

    const state = { user: null, posts: null,};

    Estamos configurando el valor predeterminado de state, que es un objeto que contiene usery postscon sus valores iniciales como null.

    Acciones #

    Las acciones son funciones que se utilizan para commituna mutación para cambiar el estado o pueden usarse para, por dispatchejemplo, llamar a otra acción. Puede ser llamado en diferentes componentes o puntos de vista y luego comete mutaciones de nuestro estado;

    Registrar acción

    Nuestra Registeracción toma datos del formulario, envía los datos a nuestro /registerpunto final y asigna la respuesta a una variable response. A continuación, enviaremos nuestro formulario usernamey passwordnuestra loginacción. De esta manera, iniciamos sesión en el usuario después de que se registra, para que sea redirigido a la /postspágina.

    async Register({dispatch}, form) { await axios.post('register', form) let UserForm = new FormData() UserForm.append('username', form.username) UserForm.append('password', form.password) await dispatch('LogIn', UserForm)},

    Acción de inicio de sesión

    Aquí es donde ocurre la autenticación principal. Cuando un usuario completa su nombre de usuario y contraseña, se pasa a un Userobjeto FormData, la LogInfunción toma el Userobjeto y realiza una POSTsolicitud al /loginpunto final para iniciar sesión como usuario.

    La Loginfunción finalmente compromete usernamela setUsermutación.

    async LogIn({commit}, User) { await axios.post('login', User) await commit('setUser', User.get('username'))},

    Crear acción posterior

    Nuestra CreatePostacción es una función que toma posty la envía a nuestro /postpunto final y luego envía la GetPostsacción. Esto permite al usuario ver sus publicaciones después de su creación.

    async CreatePost({dispatch}, post) { await axios.post('post', post) await dispatch('GetPosts')},

    Obtener acción de publicaciones

    Nuestra GetPostsacción envía una GETsolicitud a nuestro /postspunto final para recuperar las publicaciones en nuestra API y confirma setPostsla mutación.

    async GetPosts({ commit }){ let response = await axios.get('posts') commit('setPosts', response.data)},

    Acción de cerrar sesión

    async LogOut({commit}){ let user = null commit('logout', user)}

    Nuestra LogOutacción lo elimina userdel caché del navegador. Lo hace cometiendo un logout:

    Mutaciones #

    const mutations = { setUser(state, username){ state.user = username }, setPosts(state, posts){ state.posts = posts }, LogOut(state){ state.user = null state.posts = null },};

    Cada mutación toma el statey un valor de la acción que la comete, además de Logout. El valor obtenido se utiliza para cambiar ciertas partes o todas o como LogOutestablecer todas las variables a nulas.

     

    Obtenedores #

    Los captadores son funcionalidades para obtener el estado. Se puede utilizar en varios componentes para obtener el estado actual. La isAuthenticatatedfunción comprueba si state.userestá definido o es nulo y devuelve trueo falserespectivamente. StatePostsy StateUserretorno state.postsy state.uservalor respectivamente.

    const getters = { isAuthenticated: state = !!state.user, StatePosts: state = state.posts, StateUser: state = state.user,};

    Ahora todo auth.jsel archivo debería parecerse a mi código en GitHub .

    • Aproveche la sólida recuperación de datos y el tamaño de paquete optimizado con KendoReact Server Data Grid Probar ahora

    Configuración de componentes #

    1. NavBar.vueY App.vuecomponentes #

    En su src/componentscarpeta, elimine el archivo HelloWorld.vuey un nuevo archivo llamado NavBar.vue.

    Este es el componente de nuestra barra de navegación, enlaza a diferentes páginas de nuestro componente que se han enrutado aquí. Cada enlace del enrutador apunta a una ruta/página en nuestra aplicación.

    Es v-if="isLoggedIn"una condición para mostrar el Logoutenlace si un usuario ha iniciado sesión y ocultar las rutas Registery Login. Tenemos un logoutmétodo al que solo pueden acceder los usuarios que han iniciado sesión; se llamará cuando Logoutse haga clic en el enlace. Enviará la LogOutacción y luego dirigirá al usuario a la página de inicio de sesión.

    template div id="nav" router-link to="/"Home/router-link | router-link to="/posts"Posts/router-link | span v-if="isLoggedIn" a @click="logout"Logout/a /span span v-else router-link to="/register"Register/router-link | router-link to="/login"Login/router-link /span /div/templatescriptexport default { name: 'NavBar', computed : { isLoggedIn : function(){ return this.$store.getters.isAuthenticated} }, methods: { async logout (){ await this.$store.dispatch('LogOut') this.$router.push('/login') } },}/scriptstyle#nav { padding: 30px;}#nav a { font-weight: bold; color: #2c3e50;}a:hover { cursor: pointer;}#nav a.router-link-exact-active { color: #42b983;}/style

    Ahora edite su App.vuecomponente para que se vea así: Significado de los nombres

    template div id="app" NavBar / router-view/ /div/templatescript// @ is an alias to /srcimport NavBar from '@/components/NavBar.vue'export default { components: { NavBar }}/scriptstyle#app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50;}/style

    Aquí importamos el componente NavBar que creamos anteriormente y lo colocamos en la sección de plantilla antes del archivo router-view /.

     

    2. Componentes de vistas #

    Los componentes de vistas son páginas diferentes en la aplicación que se definirán en una ruta y a las que se puede acceder desde la barra de navegación. Para comenzar Vaya a la viewscarpeta, elimine el About.vuecomponente y agregue los siguientes componentes:

    • Home.vue
    • Register.vue
    • Login.vue
    • Posts.vue

    Home.vue#

    Reescribe el Home.vuepara que se vea así:

    template div class="home" pHeyyyyyy welcome to our blog, check out our posts/p /div/templatescriptexport default { name: 'Home', components: { }}/script

    Esto mostrará un texto de bienvenida a los usuarios cuando visiten la página de inicio.

    Register.vue#

    Esta es la página en la que queremos que nuestros usuarios puedan registrarse en nuestra aplicación. Cuando los usuarios completan el formulario, su información se envía a la API y se agrega a la base de datos y luego inician sesión.

    En cuanto a la API, el /registerpunto final requiere un y usernamede nuestro usuario. Ahora creemos una página y un formulario para obtener esa información:full_namepassword

    template div class="register" div form @submit.prevent="submit" div label for="username"Username:/label input type="text" name="username" v-model="form.username" /div div label for="full_name"Full Name:/label input type="text" name="full_name" v-model="form.full_name" /div div label for="password"Password:/label input type="password" name="password" v-model="form.password" /div button type="submit" Submit/button /form /div p v-if="showError" id="error"Username already exists/p /div/template

    En el Registercomponente, necesitaremos llamar a la Registeracción que recibirá los datos del formulario.

    scriptimport { mapActions } from "vuex";export default { name: "Register", components: {}, data() { return { form: { username: "", full_name: "", password: "", }, showError: false }; }, methods: { ...mapActions(["Register"]), async submit() { try { await this.Register(this.form); this.$router.push("/posts"); this.showError = false } catch (error) { this.showError = true } }, },};/script

    Empezamos importando mapActionsdesde Vuex, esto lo que hace es importar acciones de nuestra tienda al componente. Esto nos permite llamar a la acción desde el componente.

    data()contiene el valor del estado local que se utilizará en este componente, tenemos un formobjeto que contiene y username, con sus valores iniciales establecidos en una cadena vacía. También tenemos que es un booleano, que se usará para mostrar un error o no.full_namepasswordshowError

    En methodsimportamos la Registeracción usando Mapactionsal componente, por lo que la Registeracción se puede llamar con this.Register.

     

    Tenemos un método de envío que llama a la Registeracción a la que tenemos acceso this.Register, enviándola this.form. Si no errorse encuentra, utilizamos this.$routerpara enviar al usuario a la página de inicio de sesión. De lo contrario, lo configuramos showErrorcomo verdadero.

    Una vez hecho esto, podemos incluir algo de estilo.

    style scoped* { box-sizing: border-box;}label { padding: 12px 12px 12px 0; display: inline-block;}button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px;}button[type=submit]:hover { background-color: #45a049;}input { margin: 5px; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px;}#error { color: red;}/style

    Login.vue#

    Nuestra página de inicio de sesión es donde los usuarios registrados ingresarán su cuenta usernamey passwordserán autenticados por la API e iniciarán sesión en nuestro sitio.

    template div class="login" div form @submit.prevent="submit" div label for="username"Username:/label input type="text" name="username" v-model="form.username" / /div div label for="password"Password:/label input type="password" name="password" v-model="form.password" / /div button type="submit"Submit/button /form p v-if="showError" id="error"Username or Password is incorrect/p /div /div/template

    Ahora tendremos que pasar los datos de nuestro formulario a la acción que envía la solicitud y luego enviarlos a la página segura.Posts

    scriptimport { mapActions } from "vuex";export default { name: "Login", components: {}, data() { return { form: { username: "", password: "", }, showError: false }; }, methods: { ...mapActions(["LogIn"]), async submit() { const User = new FormData(); User.append("username", this.form.username); User.append("password", this.form.password); try { await this.LogIn(User); this.$router.push("/posts"); this.showError = false } catch (error) { this.showError = true } }, },};/script

    Lo importamos Mapactionsy lo usamos para importar la LogInacción al componente, que se usará en nuestra submitfunción.

    Después de la Loginacción, el usuario es redirigido a la /postspágina. En caso de error, el error se detecta y ShowErrorse establece en verdadero.

    Ahora, algo de estilo:

    style scoped* { box-sizing: border-box;}label { padding: 12px 12px 12px 0; display: inline-block;}button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px;}button[type=submit]:hover { background-color: #45a049;}input { margin: 5px; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px;}#error { color: red;}/style

    Posts.vue#

    Nuestra página de Publicaciones es la página segura que solo está disponible para usuarios autenticados. En esta página, obtienen acceso a las publicaciones en la base de datos de la API. Esto permite a los usuarios tener acceso a las publicaciones y también les permite crear publicaciones en la API.

     

    template div class="posts" div v-if="User" pHi {{User}}/p /div div form @submit.prevent="submit" div label for="title"Title:/label input type="text" name="title" v-model="form.title" /div div textarea name="write_up" v-model="form.write_up" placeholder="Write up..."/textarea /div button type="submit" Submit/button /form /div div class="posts" v-if="Posts" ul li v-for="post in Posts" :key="post.id" div id="post-div" p{{post.title}}/p p{{post.write_up}}/p pWritten By: {{post.author.username}}/p /div /li /ul /div div v-else Oh no!!! We have no posts /div /div/template

    En el código anterior, tenemos un formulario para que el usuario pueda crear nuevas publicaciones. Enviar el formulario debería hacer que la publicación se envíe a la API; agregaremos el método que lo hace en breve. También tenemos una sección que muestra publicaciones obtenidas de la API (en caso de que el usuario tenga alguna). Si el usuario no tiene ninguna publicación, simplemente mostramos un mensaje de que no hay publicaciones.

    Los captadores StateUsery StatePostsse asignan, es decir, se importan usando mapGettersinto Posts.vuey luego se pueden llamar en la plantilla.

    scriptimport { mapGetters, mapActions } from "vuex";export default { name: 'Posts', components: { }, data() { return { form: { title: '', write_up: '', } }; }, created: function () { // a function to call getposts action this.GetPosts() }, computed: { ...mapGetters({Posts: "StatePosts", User: "StateUser"}), }, methods: { ...mapActions(["CreatePost", "GetPosts"]), async submit() { try { await this.CreatePost(this.form); } catch (error) { throw "Sorry you can't make a post now!" } }, }};/script

    Tenemos un estado inicial para form, que es un objeto que tiene titley write_upcomo claves y los valores se establecen en una cadena vacía. Estos valores cambiarán a lo que el usuario ingrese en el formulario en la sección de plantilla de nuestro componente.

    Cuando el usuario envía la publicación, llamamos al this.CreatePostobjeto que recibe el formulario.

    Como puede ver en el createdciclo de vida, tenemos this.GetPostsque buscar publicaciones cuando se crea el componente.

    Un poco de estilo,

    style scoped* { box-sizing: border-box;}label { padding: 12px 12px 12px 0; display: inline-block;}button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px; margin: 10px;}button[type=submit]:hover { background-color: #45a049;}input { width:60%; margin: 15px; border: 0; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px;}textarea { width:75%; resize: vertical; padding:15px; border-radius:15px; border:0; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); height:150px; margin: 15px;}ul { list-style: none;}#post-div { border: 3px solid #000; width: 500px; margin: auto; margin-bottom: 5px;;}/style

    2. Definición de rutas

    En nuestro router/index.jsarchivo importar nuestras vistas y definir rutas para cada una de ellas.

     

    import Vue from 'vue'import VueRouter from 'vue-router'import store from '../store';import Home from '../views/Home.vue'import Register from '../views/Register'import Login from '../views/Login'import Posts from '../views/Posts'Vue.use(VueRouter)const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/register', name: "Register", component: Register, meta: { guest: true }, }, { path: '/login', name: "Login", component: Login, meta: { guest: true }, }, { path: '/posts', name: Posts, component: Posts, meta: {requiresAuth: true}, }]const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes})export default router

    3. Manejo de usuarios

    • Usuarios no autorizados
      Si notó que al definir las rutas de nuestras publicaciones agregamos una metaclave para indicar que el usuario debe estar autenticado, ahora necesitamos tener un router.BeforeEachprotector de navegación que verifique si una ruta tiene la meta: {requiresAuth: true}clave. Si una ruta tiene la metaclave, busca en la tienda un token; si está presente, los redirige a la loginruta.
    const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes})router.beforeEach((to, from, next) = { if(to.matched.some(record = record.meta.requiresAuth)) { if (store.getters.isAuthenticated) { next() return } next('/login') } else { next() }})export default router
    • Usuarios autorizados
      También tenemos un metaen las /registerrutas /login. Impide que meta: {guest: true}los usuarios que han iniciado sesión accedan a las rutas con el guestmeta.
    router.beforeEach((to, from, next) = { if (to.matched.some((record) = record.meta.guest)) { if (store.getters.isAuthenticated) { next("/posts"); return; } next(); } else { next(); }});

    Al final, tu archivo debería quedar así :

    import Vue from "vue";import VueRouter from "vue-router";import store from "../store";import Home from "../views/Home.vue";import Register from "../views/Register";import Login from "../views/Login";import Posts from "../views/Posts";Vue.use(VueRouter);const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/register", name: "Register", component: Register, meta: { guest: true }, }, { path: "/login", name: "Login", component: Login, meta: { guest: true }, }, { path: "/posts", name: "Posts", component: Posts, meta: { requiresAuth: true }, },];const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes,});router.beforeEach((to, from, next) = { if (to.matched.some((record) = record.meta.requiresAuth)) { if (store.getters.isAuthenticated) { next(); return; } next("/login"); } else { next(); }});router.beforeEach((to, from, next) = { if (to.matched.some((record) = record.meta.guest)) { if (store.getters.isAuthenticated) { next("/posts"); return; } next(); } else { next(); }});export default router;

    4.Manejo de tokens vencidos (solicitudes prohibidas) #

    Nuestra API está configurada para que los tokens caduquen después de 30 minutos. Ahora, si intentamos acceder a la postspágina después de 30 minutos, obtenemos un 401error, lo que significa que tenemos que iniciar sesión nuevamente, por lo que configuraremos un interceptor que lea si obtenemos un 401error. nos redirige de nuevo a la loginpágina.

    Agregue el siguiente fragmento después de la declaración de URL predeterminada de Axios en el main.jsarchivo.

    axios.interceptors.response.use(undefined, function (error) { if (error) { const originalRequest = error.config; if (error.response.status === 401 !originalRequest._retry) { originalRequest._retry = true; store.dispatch('LogOut') return router.push('/login') } }})

    Esto debería llevar su código al mismo estado que el ejemplo en GitHub .

    Conclusión #

    Si ha podido segu






    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Autenticación en Vue.js

    Autenticación en Vue.js

    Implemente rápidamente. Implementar inteligentemente Patrones de diseño para interfaces de IA, con Vitaly Friedman Índice

    programar

    es

    https://aprendeprogramando.es/static/images/programar-autenticacion-en-vue-1069-0.jpg

    2024-05-21

     

    Autenticación en Vue.js
    Autenticación en Vue.js

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20