基于 Express+JWT + Vue 的前后端分离架构

基于 Express、JWT 和 Vue 的前后端分离架构是一种常见的现代 Web 开发架构。这种架构将前端和后端分开部署,前端使用 Vue.js 框架构建用户界面,后端使用 Express.js 框架处理业务逻辑和提供 API 接口,JWT(JSON Web Token)用于用户的身份验证和授权。

以下是实现这种架构的基本步骤:

一、后端(Express + JWT)

初始化项目

bash 复制代码
mkdir backend
cd backend
npm init -y
npm install express jsonwebtoken body-parser cors mongoose

设置 Express 服务器

javascript 复制代码
// 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)

初始化项目

bash 复制代码
mkdir frontend
cd frontend
npm install -g @vue/cli
vue create my-project
cd my-project
npm install axios

配置 Vue 项目

javascript 复制代码
// 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');

创建登录组件

javascript 复制代码
<!-- 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>

创建受保护组件

javascript 复制代码
<!-- 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>

配置路由

javascript 复制代码
// 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,
    },
  ],
});

三、运行项目

启动后端服务器

bash 复制代码
cd backend
node server.js

启动前端应用

bash 复制代码
cd frontend/my-project
npm run serve
复制代码
相关推荐
今天AI了吗23 分钟前
什么是 AI Agent?它与直接调用大模型 API 有何区别
java·网络·人工智能·架构·java-ee
一尘之中30 分钟前
深入解析面向服务的架构(SOA):从特性到实施
学习·架构·ai写作
嘟嘟嘟95271 小时前
AI Agent 的边缘困境
人工智能·架构·agent
学渣超1 小时前
记一次复杂业务功能重构——从过程式长链路,到模板方法构建体系
java·架构·代码规范
星禾元亨1 小时前
生成式 AI 时代的架构演进与 GEO 优化:实体企业 AI 落地避坑指南
大数据·人工智能·架构·自动化·创业创新
Bs_MoneyMagnet1 小时前
基于springboot+vue的旅游景区点评系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·旅游
这个DBA有点耶1 小时前
MySQL大表DDL锁表锁到崩溃?Online DDL的3个关键参数和实战避坑
数据库·mysql·架构
依依东望9612 小时前
还在用 Electron?6 种跨平台桌面方案横评:Rust + Vue 把安装包从 224MB 干到 4.7MB
架构
ID34610744202 小时前
【课程设计】基于Spring Boot+Vue的校园共享无人机服务系统设计与实现-计算机毕设 附源码44219
javascript·vue.js·spring boot·python·node.js·php·课程设计
BillKu2 小时前
Vue3 可拖拽分栏布局:两种实现方式详解
javascript·vue.js·ecmascript