Pourquoi Securiser une application Vue ?
La sécurité est un aspect critique à considérer dans le développement d'applications Web, y compris celles basées sur Vue.js. Dans un monde où les cyberattaques sont de plus en plus sophistiquées et fréquentes, il est essentiel de protéger les données des utilisateurs et la confidentialité de l'application. Un cas concret d'importance serait une application de gestion financière où les informations personnelles et financières peuvent être sensibles.
Prerequis
- Connaissances en JavaScript et ES6+
- Familiarité avec Vue.js 2 ou 3
- Compréhension des concepts fondamentaux de Vue (components, props, events)
- Installation de Node.js et npm (https://nodejs.org/)
- Un éditeur de code (VSCode recommandé)
Concepts fondamentaux
1. Authentification et Autorisation
L'authentification vérifie l'identité d'un utilisateur, tandis que l'autorisation détermine les actions qu'un utilisateur peut effectuer sur l'application.
Schéma mental :
Authentification -> Vérification de la validité des identifiants (user/password)
Autorisation -> Attribution des droits d'accès basés sur les rôles
2. Cryptage des données sensibles
Les données sensibles doivent être cryptées avant leur stockage et/ou transmission pour prévenir toute usurpation.
Schéma mental :
Données sensibles -> Cryptage avant le stockage/transmission
Données chiffrées -> Décryptage pour l'utilisation
3. Utilisation de HTTPS
Le protocole HTTP sécurisé (HTTPS) assure la confidentialité et l'intégrité des communications entre le client et le serveur.
Schéma mental :
HTTP (non sécurisé) -> HTTPS (sécurisé)
Mise en pratique : projet fil rouge
Nous allons créer un simple gestionnaire de tâches qui permet aux utilisateurs de s'inscrire, de se connecter et d'ajouter/supprimer des tâches.
Structure du projet :
task-manager/
├── public/
│ ├── index.html
├── src/
│ ├── assets/
│ ├── components/
│ ├── TaskList.vue
│ ├── TaskForm.vue
│ ├── App.vue
│ ├── main.js
│ ├── store.js (Vuex)
│ ├── router.js (Vue Router)
├── package.json
Étape 1 : Initialisation du projet
npm init -y
npm install vue@next vue-router@next vuex@next axios
main.js :
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
createApp(App)
.use(router)
.use(store)
.mount('#app');
router.js :
import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';
import Login from './views/Login.vue';
import Tasks from './views/Tasks.vue';
const routes = [
{ path: '/', component: Home },
{ path: '/login', component: Login },
{ path: '/tasks', component: Tasks, meta: { requiresAuth: true } }
];
const router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.isAuthenticated) {
next({ path: '/login' });
} else {
next();
}
} else {
next();
}
});
export default router;
store.js :
import { createStore } from 'vuex';
export default createStore({
state: {
isAuthenticated: false,
user: null
},
mutations: {
SET_AUTH(state, status) {
state.isAuthenticated = status;
},
SET_USER(state, user) {
state.user = user;
}
},
actions: {
login({ commit }, user) {
// Simulate API call
setTimeout(() => {
commit('SET_AUTH', true);
commit('SET_USER', user);
localStorage.setItem('user', JSON.stringify(user));
}, 1000);
},
logout({ commit }) {
commit('SET_AUTH', false);
commit('SET_USER', null);
localStorage.removeItem('user');
}
}
});
Étape 2 : Création des composants
TaskForm.vue :
<template>
<form @submit.prevent="addTask">
<input v-model="taskName" placeholder="Add new task" />
<button type="submit">Submit</button>
</form>
</template>
<script>
export default {
data() {
return {
taskName: ''
};
},
methods: {
addTask() {
// Simulate API call
this.$store.dispatch('addTask', { name: this.taskName });
this.taskName = '';
}
}
};
</script>
TaskList.vue :
<template>
<ul>
<li v-for="task in tasks" :key="task.id">
task.name
<button @click="deleteTask(task.id)">Delete</button>
</li>
</ul>
</template>
<script>
export default {
computed: {
tasks() {
return this.$store.state.tasks;
}
},
methods: {
deleteTask(id) {
// Simulate API call
this.$store.dispatch('deleteTask', id);
}
}
};
</script>
App.vue :
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
};
</script>
Étape 3 : Création des vues
Home.vue :
<template>
<div>
<h1>Welcome to Task Manager</h1>
<router-link to="/tasks">Go to Tasks</router-link>
</div>
</template>
<script>
export default {
name: 'Home'
};
</script>
Login.vue :
<template>
<form @submit.prevent="login">
<input v-model="username" placeholder="Username" />
<input type="password" v-model="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
</template>
<script>
export default {
data() {
return {
username: '',
password: ''
};
},
methods: {
login() {
const user = { username: this.username, password: this.password };
this.$store.dispatch('login', user);
this.$router.push('/tasks');
}
}
};
</script>
Tasks.vue :
<template>
<div>
<h1>Tasks</h1>
<TaskForm />
<TaskList />
</div>
</template>
<script>
import TaskForm from './TaskForm.vue';
import TaskList from './TaskList.vue';
export default {
name: 'Tasks',
components: {
TaskForm,
TaskList
}
};
</script>
Erreurs frequentes et debugging
1. Erreur : Cannot read property 'map' of undefined
Code incorrect :
computed: {
tasks() {
return this.$store.state.tasks.map(task => task.name);
}
}
Code correct :
computed: {
tasks() {
return this.$store.state.tasks ? this.$store.state.tasks.map(task => task.name) : [];
}
}
2. Erreur : Uncaught SyntaxError: Unexpected token 'import'
Code incorrect :
const axios = require('axios');
Code correct :
import axios from 'axios';
3. Erreur : Access Denied
Code incorrect :
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.isAuthenticated) {
next({ path: '/login' });
} else {
next();
}
} else {
next();
}
});
Code correct :
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.isAuthenticated) {
next({ path: '/login' });
} else {
next();
}
} else {
next();
}
});
Pour aller plus loin
1. Utilisation de OAuth pour une authentification plus sécurisée
- Concept : OAuth est un protocole d'autorisation qui permet aux utilisateurs de partager des informations sans divulguer leurs identifiants.
- Lien : https://oauth.net/2/
2. Cryptage des mots de passe avec Bcrypt
- Concept : Bcrypt est une bibliothèque de cryptage de mots de passe qui génère un hash sécurisé.
- Lien : https://github.com/kelektiv/node.bcrypt.js
3. Utilisation de Webpack pour la gestion des assets
- Concept : Webpack est un module bundler populaire qui permet de gérer les assets (CSS, images) dans un projet Vue.
- Lien : https://webpack.js.org/
Défi pratique
Créez une application Vue simple qui utilise l'authentification JWT pour la sécurité des utilisateurs. L'application doit permettre aux utilisateurs de s'inscrire, de se connecter et d'accéder à une page sécurisée contenant une liste de tâches. Utilisez Vuex pour gérer l'état de l'application et Vue Router pour la navigation.