基于 Express+JWT + Vue 的前后端分离架构
基于 Express、JWT 和 Vue 的前后端分离架构是一种常见的现代 Web 开发架构。这种架构将前端和后端分开部署,前端使用 Vue.js 框架构建用户界面,后端使用 Express.js 框架处理业务逻辑和提供 API 接口,JWT(JSON Web Token)用于用户的身份验证和授权。
以下是实现这种架构的基本步骤:
一、后端(Express + JWT)
初始化项目
mkdir backend
cd backend
npm init -y
npm install express jsonwebtoken body-parser cors mongoose
设置 Express 服务器
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(bodyParser.json());
app.use(cors());
// Dummy user data (in a real application, you should use a database)
const users = [
{ id: 1, username: 'testuser', password: 'testpass' },
];
// Middleware to authenticate JWT
const authenticateJWT = (req, res, next) => {
const token = req.header('Authorization').replace('Bearer ', '');
if (!token) return res.sendStatus(401);
jwt.verify(token, 'your_jwt_secret', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
};
// User login route
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (users[0].username!=username) return res.sendStatus(401);
const token = jwt.sign({ id: user.id }, 'your_jwt_secret');
res.json({ token });
});
// Protected route
app.get('/protected', authenticateJWT, (req, res) => {
res.json({ message: 'This is a protected route', user: req.user });
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
二、前端(Vue)
初始化项目
mkdir frontend
cd frontend
npm install -g @vue/cli
vue create my-project
cd my-project
npm install axios
配置 Vue 项目
// src/main.js
import Vue from 'vue';
import App from './App.vue';
import axios from 'axios';
Vue.config.productionTip = false;
Vue.prototype.$axios = axios;
new Vue({
render: h => h(App),
}).$mount('#app');
创建登录组件
<!-- src/components/Login.vue -->
<template>
<div>
<h2>Login</h2>
<form @submit.prevent="login">
<div>
<label for="username">Username:</label>
<input type="text" v-model="username" id="username" />
</div>
<div>
<label for="password">Password:</label>
<input type="password" v-model="password" id="password" />
</div>
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
username: '',
password: '',
};
},
methods: {
async login() {
try {
const response = await this.$axios.post('http://localhost:3000/login', {
username: this.username,
password: this.password,
});
const token = response.data.token;
localStorage.setItem('jwt', token);
this.$router.push('/protected');
} catch (error) {
console.error(error);
}
},
},
};
</script>
创建受保护组件
<!-- src/components/Protected.vue -->
<template>
<div>
<h2>Protected Route</h2>
<div v-if="message">{{ message }}</div>
<div v-else>Loading...</div>
</div>
</template>
<script>
export default {
data() {
return {
message: '',
};
},
created() {
this.fetchProtectedData();
},
methods: {
async fetchProtectedData() {
const token = localStorage.getItem('jwt');
if (!token) {
this.$router.push('/login');
return;
}
try {
const response = await this.$axios.get('http://localhost:3000/protected', {
headers: { Authorization: `Bearer ${token}` },
});
this.message = response.data.message;
} catch (error) {
console.error(error);
localStorage.removeItem('jwt');
this.$router.push('/login');
}
},
},
};
</script>
配置路由
// src/router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Login from '@/components/Login.vue';
import Protected from '@/components/Protected.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
redirect: '/login',
},
{
path: '/login',
name: 'Login',
component: Login,
},
{
path: '/protected',
name: 'Protected',
component: Protected,
},
],
});
三、运行项目
启动后端服务器
cd backend
node server.js
启动前端应用
cd frontend/my-project
npm run serve