nodejs+mysql+vue3 应用实例剖析

文章目录

    • node.js + vue3 +mysql 应用
      • 后端实现(koa部分)
        • 1. 项目初始化与依赖安装
        • 2. 目录结构
        • 3. `config/db.js` - 数据库配置与连接
        • 4. `models/user.js` - 用户模型及数据库操作
        • 5. `controllers/authController.js` - 认证控制器
        • 6. `controllers/userController.js` - 用户操作控制器
        • 7. `middlewares/authMiddleware.js` - `token`验证中间件
        • 8. `routes/index.js` - 首页路由
        • 9. `routes/user.js` - 用户管理路由
        • 10. `app.js` - 应用入口文件
      • 前端实现(`Vue 3`部分)
        • 1. 创建`Vue 3`项目
        • 2. 目录结构(主要部分)
        • 3. `src/main.js` - 入口文件
        • 4. `src/router/index.js` - 路由配置
        • 5. `src/components/Login.vue` - 登录组件
        • 6. `src/components/Home.vue` - 首页组件
        • 7. `src/components/Detail.vue` - 详情页组件
        • 8. `src/components/UserManagement.vue` - 用户管理组件
        • 9. `src/store/index.js` - `Vuex`存储(示例,可根据需要扩展)
      • 注意事项
    • 小结
      • 一、安全相关
      • 二、错误处理
      • 三、代码结构与可维护性
      • 四、权限控制
      • 五、前端相关
    • 补充(前端将 token 存储在 localStorage 容易遭受 XSS 攻击,应采用更安全的存储方式,具体如下:)

node.js + vue3 +mysql 应用

以下是一个使用koa作为后端(包含登录、token验证、用户增删改查和权限控制),MySQL作为数据库,Vue 3作为前端展示的示例:

后端实现(koa部分)

1. 项目初始化与依赖安装

创建后端项目文件夹,在命令行进入该文件夹后执行以下命令:

npm init -y
npm install koa koa-router koa-bodyparser jsonwebtoken mysql2 bcryptjs cors
2. 目录结构
backend
|-- app.js
|-- controllers
|   |-- authController.js
|   |-- userController.js
|-- middlewares
|   |-- authMiddleware.js
|-- routes
|   |-- index.js
|   |-- user.js
|-- models
|   |-- user.js
|-- config
|   |-- db.js
3. config/db.js - 数据库配置与连接
const mysql = require('mysql2/promise');const pool = mysql.createPool({host: 'localhost',user: 'root',password: 'your_password',database: 'your_database_name',waitForConnections: true,connectionLimit: 10,queueLimit: 0
});module.exports = pool;
4. models/user.js - 用户模型及数据库操作
const pool = require('../config/db.js');class User {static async findByUsername(username) {const [rows] = await pool.query('SELECT * FROM users WHERE username =?', [username]);return rows[0];}static async findById(id) {const [rows] = await pool.query('SELECT * FROM users WHERE id =?', [id]);return rows[0];}static async create(userData) {const { username, password, role, menus } = userData;const [result] = await pool.query('INSERT INTO users (username, password, role, menus) VALUES (?,?,?,?)', [username, password, role, menus]);return result.insertId;}static async update(id, updatedUser) {const { password, role, menus } = updatedUser;const query = 'UPDATE users SET password =?, role =?,menus =? WHERE id =?';const [result] = await pool.query(query, [password, role, menus, id]);return result.affectedRows > 0;}static async delete(id) {const [result] = await pool.query('DELETE FROM users WHERE id =?', [id]);return result.affectedRows > 0;}static async getAll() {const [rows] = await pool.query('SELECT * FROM users');return rows;}
}module.exports = User;
5. controllers/authController.js - 认证控制器
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/user.js');const login = async (ctx) => {const { username, password } = ctx.request.body;const user = await User.findByUsername(username);if (user) {const isMatch = await bcrypt.compare(password, user.password);if (isMatch) {const token = jwt.sign({ id: user.id, username: user.username, role: user.role }, 'your_secret_key', { expiresIn: '1h' });ctx.body = { success: true, token };} else {ctx.status = 401;ctx.body = { success: false, message: '密码错误' };}} else {ctx.status = 404;ctx.body = { success: false, message: '用户不存在' };}
};module.exports = {login
};
6. controllers/userController.js - 用户操作控制器
const User = require('../models/user.js');const getUsers = async (ctx) => {const users = await User.getAll();ctx.body = users;
};const getUserById = async (ctx) => {const id = ctx.params.id;const user = await User.findById(id);if (user) {ctx.body = user;} else {ctx.status = 404;ctx.body = { message: '用户不存在' };}
};const updateUser = async (ctx) => {const id = ctx.params.id;const updatedUser = ctx.request.body;const result = await User.update(id, updatedUser);if (result) {ctx.body = { message: '用户更新成功' };} else {ctx.status = 404;ctx.body = { message: '用户不存在,无法更新' };}
};const deleteUser = async (ctx) => {const id = ctx.params.id;const result = await User.delete(id);if (result) {ctx.body = { message: '用户删除成功' };} else {ctx.status = 404;ctx.body = { message: '用户不存在,无法删除' };}
};module.exports = {getUsers,getUserById,updateUser,deleteUser
};
7. middlewares/authMiddleware.js - token验证中间件
const jwt = require('jsonwebtoken');const authMiddleware = async (ctx, next) => {const token = ctx.headers.authorization && ctx.headers.authorization.split(' ')[1];if (token) {try {const decoded = jwt.verify(token, 'your_secret_key');ctx.state.user = decoded;await next();} catch (error) {ctx.status = 401;ctx.body = { success: false, message: '无效的token' };}} else {ctx.status = 401;ctx.body = { success: false, message: '未提供token' };}
};module.exports = authMiddleware;
8. routes/index.js - 首页路由
const Router = require('koa-router');
const router = new Router();router.get('/', (ctx) => {ctx.body = '这是首页';
});module.exports = router;
9. routes/user.js - 用户管理路由
const Router = require('koa-router');
const userController = require('../controllers/userController.js');
const authMiddleware = require('../middlewares/authMiddleware.js');
const router = new Router({ prefix: '/users' });router.get('/', authMiddleware, userController.getUsers);
router.get('/:id', authMiddleware, userController.getUserById);
router.put('/:id', authMiddleware, userController.updateUser);
router.delete('/:id', authMiddleware, userController.deleteUser);module.exports = router;
10. app.js - 应用入口文件
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const cors = require('cors');
const indexRouter = require('./routes/index.js');
const userRouter = require('./routes/user.js');
const authController = require('./controllers/authController.js');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();// 使用cors中间件,允许跨域请求
app.use(cors());
// 应用中间件
app.use(bodyParser());// 登录路由
router.post('/login', authController.login);// 挂载首页路由
app.use(indexRouter.routes()).use(indexRouter.allowedMethods());
// 挂载用户管理路由
app.use(userRouter.routes()).use(userRouter.allowedMethods());
// 挂载总路由
app.use(router.routes()).use(router.allowedMethods());app.listen(3000, () => {console.log('后端服务器启动,监听3000端口');
});

前端实现(Vue 3部分)

1. 创建Vue 3项目

使用@vue/cli创建项目(如果没有安装,先执行npm install -g @vue/cli):

vue create frontend

在创建项目过程中选择Vue 3相关选项。

2. 目录结构(主要部分)
frontend
|-- src
|   |-- components
|   |   |-- Login.vue
|   |   |-- Home.vue
|   |   |-- Detail.vue
|   |   |-- UserManagement.vue
|   |-- router
|   |   |-- index.js
|   |-- store
|   |   |-- index.js
|   |-- main.js
3. src/main.js - 入口文件
import { createApp } from 'vue';
import App from './App.vue';
import router from './router/index';
import store from './store/index';createApp(App).use(router).use(store).mount('#app');
4. src/router/index.js - 路由配置
import { createRouter, createWebHistory } from 'vue-router';
import Login from '../components/Login.vue';
import Home from '../components/Home.vue';
import Detail from '../components/Detail.vue';
import UserManagement from '../components/UserManagement.vue';const routes = [{ path: '/', redirect: '/login' },{ path: '/login', component: Login },{ path: '/home', component: Home },{ path: '/detail', component: Detail },{ path: '/user-management', component: UserManagement }
];const router = createRouter({history: createWebHistory(),routes
});export default router;
5. src/components/Login.vue - 登录组件
<template><div><input v-model="username" placeholder="用户名" /><input type="password" v-model="password" placeholder="密码" /><button @click="login">登录</button></div>
</template><script>
import axios from 'axios';export default {data() {return {username: '',password: ''};},methods: {async login() {try {const response = await axios.post('http://localhost:3000/login', {username: this.username,password: this.password});if (response.data.success) {localStorage.setItem('token', response.data.token);this.$router.push('/home');}} catch (error) {console.error('登录失败', error);}}}
};
</script>
6. src/components/Home.vue - 首页组件
<template><div>这是首页</div>
</template><script>
export default {// 这里可以添加需要的逻辑,比如获取用户信息(如果有需要)
};
</script>
7. src/components/Detail.vue - 详情页组件
<template><div>这是详情页</div>
</template><script>
export default {// 这里可以添加详情页相关逻辑
};
</script>
8. src/components/UserManagement.vue - 用户管理组件
<template><div><button v-if="user.role === 'admin'" @click="getUsers">获取用户列表</button><!-- 这里可以添加更多用户管理相关的UI和操作 --></div>
</template><script>
import axios from 'axios';export default {data() {return {user: {},users: []};},created() {const token = localStorage.getItem('token');if (token) {axios.get('http://localhost:3000/users', {headers: {Authorization: 'Bearer'+ token}}).then(response => {this.user = response.data;}).catch(error => {console.error('获取用户信息失败', error);});}},methods: {async getUsers() {const token = localStorage.getItem('token');try {const response = axios.get('http://localhost:3000/users', {headers: {Authorization: 'Bearer'+ token}});this.users = (await response).data;} catch (error) {console.error('获取用户列表失败', error);}}}
};
</script>
9. src/store/index.js - Vuex存储(示例,可根据需要扩展)
import { createStore } from 'vuex';const store = createStore({state() {return {user: null};},mutations: {setUser(state, user) {state.user = user;}},actions: {// 可以添加获取用户信息等相关的异步操作}
});export default store;

注意事项

  1. 在实际应用中,需要对密码进行更安全的处理,比如使用更强的bcrypt哈希算法参数。
  2. 对于token的存储和验证机制,可以进一步优化,比如添加token刷新逻辑。
  3. 前端的axios请求可以添加更多的错误处理和加载状态提示。
  4. 权限控制在前端可以根据后端返回的用户角色和菜单权限更精细地控制组件的显示和隐藏。

小结

以下是上述代码实例直接应用时需要注意的一些总结与建议:

一、安全相关

  1. 密码加密强度
    • 注意事项:在后端的 authController.js 中使用 bcryptjs 进行密码加密时,应合理设置加密成本因子(cost 参数)来增强密码安全性。默认的 cost 值可能不足以抵御强力的暴力破解攻击。
    • 实例剖析:例如,当前代码可能像这样使用 bcrypt 比较密码(简化示例):
    const isMatch = await bcrypt.compare(password, user.password);
    
    但没有设置 cost 参数,实际应用中可在密码创建或更新时明确设置合适的强度,像这样:
    const saltRounds = 12; // 合适的成本因子,可根据服务器性能等调整
    const hashedPassword = await bcrypt.hash(newPassword, saltRounds);
    
  2. Token 管理
    • 注意事项token 的生成与验证使用固定的 secret_key,且缺少刷新机制,同时存储和传输过程也需更安全的处理。
    • 实例剖析
      • authController.js 中生成 token 时:
      const token = jwt.sign({ id: user.id, username: user.username,role: user.role }, 'your_secret_key', { expiresIn: '1h' });
      
      这里的 your_secret_key 应配置为环境变量,避免硬编码在代码中导致泄露风险,比如在服务器启动时通过环境变量读取:
      const secretKey = process.env.JWT_SECRET_KEY;
      const token = jwt.sign({... }, secretKey, { expiresIn: '1h' });
      
      • 关于 token 刷新,当 token 快过期时,前端没有机制向后台请求新的 token 来无缝续期,用户体验不佳,需要实现类似的逻辑,例如设置定时器,在 token 有效期的一定时间(如还剩 10 分钟过期时)向专门的 token 刷新接口发起请求获取新 token
  3. 数据库连接安全
    • 注意事项:数据库连接配置中用户名、密码等信息硬编码不安全,并且缺少完善的错误处理和连接池管理。
    • 实例剖析:在 config/db.js 中:
      const pool = mysql.createPool({host: 'localhost',user: 'root',password: 'your_password',database: 'your_database_name',//... 其他配置
      });
      
      应改为通过环境变量来设置这些敏感信息,如:
      const pool = mysql.createPool({host: process.env.DB_HOST,user: process.env.DB_USER,password: process.env.DB_PASSWORD,database: process.env.DB_NAME,//... 其他配置
      });
      
      同时,对于数据库操作可能出现的错误(如连接失败、查询超时等),目前只是简单返回结果,没有针对性的错误处理,例如在 models/user.js 中查询用户时,如果数据库连接出现问题,应进行更好的错误捕获和处理,像这样:
      static async findByUsername(username) {try {const [rows] = await pool.query('SELECT * FROM users WHERE username =?', [username]);return rows[0];} catch (error) {console.error('数据库查询用户出错:', error);throw new Error('数据库查询用户时遇到问题,请稍后再试');}
      }
      

二、错误处理

  1. 统一错误处理机制
    • 注意事项:后端各个功能模块中的错误处理较分散、简单,缺乏统一规范,不利于排查和定位问题。
    • 实例剖析:比如在 userController.js 中不同操作(如获取用户、更新用户等)都各自返回简单的错误信息,像更新用户操作:
      const updateUser = async (ctx) => {const id = ctx.params.id;const updatedUser = ctx.request.body;const result = await User.update(id, updatedUser);if (result) {ctx.body = { message: '用户信息更新成功' };} else {ctx.status = 404;ctx.body = { message: '用户不存在,无法更新' };}
      };
      
      可以创建一个统一的错误处理中间件,将各种错误进行分类,返回更规范、详细的错误响应给前端,例如创建 errorMiddleware.js
      module.exports = async (ctx, next) => {try {await next();} catch (error) {ctx.status = error.statusCode || 500;ctx.body = {success: false,message: error.message || '服务器内部错误'};}
      };
      
      然后在 app.js 中应用这个中间件,使整个应用能统一处理错误情况。

三、代码结构与可维护性

  1. 分层与模块化
    • 注意事项:随着业务扩展,目前代码的分层不够清晰,业务逻辑分散在控制器和模型中,不利于维护和扩展,可添加服务层来封装业务逻辑。
    • 实例剖析:例如用户注册功能,如果后续需要添加更多复杂逻辑(如验证手机号格式、发送注册短信验证码等),目前这些逻辑可能会散落在控制器里的注册方法中,更好的做法是创建 userService.js 服务层文件,在里面封装注册相关的完整逻辑,像这样:
    const User = require('./models/user.js');
    const bcrypt = require('bcryptjs');
    const register = async (userData) => {// 验证手机号等额外逻辑(此处省略具体实现)const hashedPassword = await bcrypt.hash(userData.password, 10);const newUser = {username: userData.username,password: hashedPassword,role: userData.role,menus: userData.menus};return User.create(newUser);
    };
    module.exports = {register
    };
    
    然后在 authController.js 中调用这个服务层方法来进行注册操作,使得业务逻辑更加清晰,便于后续维护和扩展。
  2. 数据库操作可移植性
    • 注意事项:直接写 SQL 语句进行数据库操作,后期若更换数据库(如从 MySQL 换为 PostgreSQL),改动成本大,可考虑引入 ORM 框架。
    • 实例剖析:当前在 models/user.js 中有很多原生 SQL 查询语句,如:
    static async findByUsername(username) {const [rows] = await pool.query('SELECT * FROM users WHERE username =?', [username]);return rows[0];
    }
    
    若使用 Sequelize(一种常见 ORM 框架),代码可以改为:
    const { Model, DataTypes } = require('sequelize');
    const sequelize = require('../config/database');class User extends Model {}User.init({id: {type: DataTypes.INTEGER,autoIncrement: true,primaryKey: true},username: {type: DataTypes.STRING,allowNull: false,unique: true},password: {type: DataTypes.STRING,allowNull: false},role: {type: DataTypes.STRING,allowNull: false},menus: {type: DataTypes.TEXT},created_at: {type: DataTypes.DATE,defaultValue: DataTypes.NOW},updated_at: {type: DataTypes.DATE,defaultValue: DataTypes.NOW,onUpdate: DataTypes.NOW}
    }, {sequelize,modelName: 'User'
    });module.exports = User;
    
    这样后续切换数据库时,只需调整 Sequelize 的配置,而不用大量修改具体的数据库操作语句。

四、权限控制

  1. 细粒度权限
    • 注意事项:当前基于用户角色和菜单的权限控制较粗,实际可能需要更细粒度,比如对具体功能按钮等操作进行权限管控。
    • 实例剖析:在前端 UserManagement.vue 组件中,目前只是简单根据角色判断是否显示获取用户列表按钮:
    <button v-if="user.role === 'admin'" @click="getUsers">获取用户列表</button>
    
    但可能对于“删除用户”这个操作,不仅要判断角色是 admin,还需要后端进一步返回针对每个用户是否有删除权限的具体标识,前端根据这个标识来决定按钮是否可用,后端则在对应的删除用户接口处详细校验这个权限,比如在 userController.jsdeleteUser 方法中添加更细的权限判断逻辑:
    const deleteUser = async (ctx) => {const id = ctx.params.id;const user = ctx.state.user;if (user.role === 'admin' && user.permissions.includes('delete_user')) {const result = await User.delete(id);if (result) {ctx.body = { message: '用户删除成功' };} else {ctx.status = 404;ctx.body = { message: '用户不存在,无法删除' };}} else {ctx.status = 403;ctx.body = { message: '您没有权限执行此操作' };}
    };
    
  2. 权限数据校验
    • 注意事项:后端没有对前端传递的权限相关数据严格校验,存在前端篡改数据越权访问风险。
    • 实例剖析:当前前端获取用户信息后可以拿到权限相关数据(如角色、菜单权限等),若前端恶意篡改这些数据再发起请求,后端没有有效校验机制就可能导致越权访问。比如在后端的 authMiddleware.js 中验证 token 后获取用户信息时,可以增加对权限数据格式、范围等的校验逻辑,确保其合法性,像这样:
    const authMiddleware = async (ctx, next) => {const token = ctx.headers.authorization && ctx.headers.authorization.split(' ')[1];if (token) {try {const decoded = jwt.verify(token,'secret_key');// 校验权限相关字段是否符合预期格式和范围if (typeof decoded.role ==='string' && Array.isArray(decoded.menus)) {ctx.state.user = decoded;await next();} else {ctx.status = 401;ctx.body = { success: false, message: '权限数据格式错误' };}} catch (error) {ctx.status = 401;ctx.body = { success: false, message: '无效的token' };}} else {ctx.status = 401;ctx.body = { success: false, message: '未提供token' };}
    };
    

五、前端相关

  1. Token 存储安全

    • 注意事项:前端将 token 存储在 localStorage 容易遭受 XSS 攻击,应采用更安全的存储方式。
    • 实例剖析:在 Login.vue 组件中登录成功后这样存储 token
    localStorage.setItem('token', response.data.token);
    

    可以考虑使用 cookie 并设置 httpOnly 属性来存储 token,在后端设置 cookie 相关属性(例如通过 koa 的中间件来设置),让前端无法通过脚本访问到 token,增强安全性。

  2. 用户输入验证

    • 注意事项:前端对用户输入验证简单,容易被利用发起恶意攻击,应加强输入框的验证。
    • 实例剖析:在 Login.vue 组件中,用户名和密码输入框没有限制输入内容,像这样:
    <input v-model="username" placeholder="用户名" />
    <input type="password" v-model="password" placeholder="密码" />
    

    可以添加 v-validate(使用 vee-validate 等验证库)等方式来限制输入长度、格式等,例如:

    <input v-model="username" placeholder="用户名" 
    v-validate="'required|min:3|max:20'" />
    <input type="password" v-model="password" 
    placeholder="密码" v-validate="'required|min:6|max:20'" />
    

    并根据验证结果给出相应提示,防止不合理的输入传递到后端。

  3. 接口请求优化与统一处理

    • 注意事项:前端使用 axios 进行接口请求时,缺少统一的拦截处理,导致代码冗余且不利于维护,同时存在重复请求等性能问题。
    • 实例剖析:在多个组件(如 UserManagement.vue)中都有发起获取用户相关信息的请求,每次都要写类似这样的代码:
    const token = localStorage.getItem('token');
    try {const response = axios.get('http://localhost:3000/users', {headers: {Authorization: 'Bearer'+ token}});this.users = (await response).data;
    } catch (error) {console.error('获取用户列表失败', error);
    }
    

    可以创建 axios 实例并设置统一的请求拦截器(添加请求头、处理加载状态等)和响应拦截器(处理错误、统一解析数据等),例如在 src/api/index.js 创建:

    import axios from 'axios';const instance = axios.create({baseURL: 'http://localhost:3000'
    });instance.interceptors.request.use((config) => {const token = localStorage.getItem('token');if (token) {config.headers.Authorization = `Bearer ${token}`;}return config;
    }, (error) => {return Promise.reject(error);
    });instance.interceptors.response.use((response) => {return response.data;
    }, (error) => {console.error('接口请求出错:', error);return Promise.reject(error);
    });export default instance;
    

    然后在组件中直接使用这个 axios 实例进行请求,减少重复代码,也方便对请求进行统一管理和优化。

总之,上述代码示例要直接应用到实际生产环境,需要从安全、错误处理、代码结构、权限控制以及前端相关等多方面进行完善和优化,以保障应用的稳定、安全和可维护性。

补充(前端将 token 存储在 localStorage 容易遭受 XSS 攻击,应采用更安全的存储方式,具体如下:)

  1. HttpOnly Cookie
    • 原理HttpOnly 是一个设置在 Cookie 上的属性,当一个 Cookie 被标记为 HttpOnly 时,通过浏览器端的脚本(如JavaScript)将无法访问该 Cookie。这样可以有效防止 XSS 攻击获取存储在 Cookie 中的敏感信息,如 token
    • 后端设置示例(以koa为例)
const Koa = require('koa');
const app = new Koa();
const cookie = require('koa-cookie');// 应用中间件
app.use(cookie());// 假设在登录成功后设置token到Cookie
const login = async (ctx) => {//... 登录验证逻辑const token = jwt.sign({... },'secret_key', { expiresIn: '1h' });ctx.cookies.set('token', token, {httpOnly: true,// 还可以设置其他属性,如maxAge(过期时间,单位为毫秒)、path(Cookie生效的路径)等maxAge: 3600 * 1000, path: '/'});ctx.body = { success: true };
};
  • 前端注意事项:由于 HttpOnly 的特性,前端无法通过 document.cookie 等方式获取 token,因此在发送需要认证的请求时,浏览器会自动将带有 HttpOnly 属性的 Cookie 包含在请求头中(前提是请求的域名、路径等符合 Cookie 设置的要求)。但要注意,这种方式在跨域场景下可能需要额外配置(如设置 withCredentials 属性为 true),同时要确保后端正确处理跨域的 Cookie
  1. Encrypted LocalStorage
    • 原理:在将 token 存储到 localStorage 之前,使用加密算法对其进行加密。这样即使攻击者能够访问 localStorage,获取到的也是加密后的内容,无法直接使用。在需要使用 token 时,再通过解密算法还原。
    • 示例代码(使用crypto-js库进行加密和解密)
    • 首先安装 crypto - js
npm install crypto-js
  • 存储 token(在登录成功后,假设在一个 Vue 组件中):
import CryptoJS from 'crypto-js';
const SECRET_KEY = 'your_secret_key'; // 用于加密和解密的密钥,要妥善保管const storeToken = (token) => {const encryptedToken = CryptoJS.AES.encrypt(token, SECRET_KEY).toString();localStorage.setItem('encrypted_token', encryptedToken);
};
  • 获取和使用 token(在需要发送认证请求的组件方法中):
const getToken = () => {const encryptedToken = localStorage.getItem('encrypted_token');if (encryptedToken) {const bytes = CryptoJS.AES.decrypt(encryptedToken, SECRET_KEY);const originalToken = bytes.toString(CryptoJS.enc.Utf8);return originalToken;}return null;
};
  • 注意事项:加密密钥的安全性至关重要,不能在代码中硬编码,应该像存储 tokensecret_key 一样,通过环境变量等方式配置。同时,加密和解密操作可能会对性能产生一定的影响,尤其是在频繁存储和获取 token 的场景下。
  1. Memory Storage(In - Memory Session)
    • 原理:将 token 存储在内存中,例如在 Vue 应用中,可以使用一个全局的变量(如 Vuex 状态管理中的状态)或者一个单例对象来存储 token。这种方式下,token 不会持久化存储在本地存储介质中,只要页面关闭或者应用退出,token 就会消失,因此可以避免一些存储层面的安全风险。
    • 示例(使用Vuex存储)
    • src/store/index.js 中:
import { createStore } from 'vuex';const store = createStore({state() {return {token: null};},mutations: {setToken(state, token) {state.token = token;}},actions: {// 例如在登录成功后设置tokenloginSuccess({ commit }, token) {commit('setToken', token);}}
});export default store;
  • 在登录组件(如 Login.vue)中:
import { mapActions } from 'vuex';
export default {methods: {...mapActions(['loginSuccess']),async login() {try {const response = await axios.post('http://localhost:3000/login', {//... 登录参数});if (response.data.success) {this.loginSuccess(response.data.token);//... 其他逻辑,如路由跳转}} catch (error) {console.error('登录失败', error);}}}
};
  • 注意事项:这种方式的缺点是,如果用户刷新页面或者意外关闭标签页后重新打开,用户可能需要重新登录获取 token,会影响用户体验。为了缓解这个问题,可以结合其他存储方式(如前面提到的加密 localStorage)来在一定程度上恢复用户状态,但这也增加了复杂性和潜在的安全风险。同时,在多标签页或者多窗口场景下,内存中的存储方式需要考虑如何在不同标签页之间同步 token 状态等问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/473860.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ASR技术】WhisperX安装使用

介绍 WhisperX 是一个开源的自动语音识别&#xff08;ASR&#xff09;项目&#xff0c;由 m-bain 开发。该项目基于 OpenAI 的 Whisper 模型&#xff0c;通过引入批量推理、强制音素对齐和语音活动检测等技术。提供快速自动语音识别&#xff08;large-v2 为 70 倍实时&#xf…

android framework ams/wms常见系统日志(main\system\events\crash,protoLog使用)

重要性 wms和ams的一些系统原生日志能够帮助我们快速定位问题 日志分类 在日常framework工作中常见的日志类别如下&#xff1a; -b , --buffer Request alternate ring buffer, ‘main’, ‘system’, ‘radio’, ‘events’, ‘crash’, ‘default’ or ‘all’. Additiona…

2024年11月16日 星期六 重新整理Go技术

今日格言 坚持每天进步一点点~ 一个人也可以是一个团队~ 学习全栈开发, 做自己喜欢的产品~~ 简介 大家好, 我是张大鹏, 今天是2024年11月16日星期六, 很高兴在这里给大家分享技术. 今天又是休息的一天, 做了很多的思考, 整理了自己掌握的技术, 比如Java, Python, Golang,…

深度解读混合专家模型(MoE):算法、演变与原理

假设一个专家团队共同解决复杂问题。每位专家都拥有独特的技能&#xff0c;团队通过高效分配任务实现了前所未有的成功。这就是混合专家&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;模型架构背后的基本思想&#xff0c;这种方法允许机器学习系统&#xff0c;特别…

Area-Composition模型部署指南

一、介绍 本模型可以通过输入不同的提示词&#xff0c;然后根据各部分提示词进行融合生成图片。如下图&#xff1a; 此图像包含 4 个不同的区域&#xff1a;夜晚、傍晚、白天、早晨 二、部署 环境要求&#xff1a; 最低显存&#xff1a;10G 1. 部署ComfyUI 本篇的模型部署…

HTML之列表学习记录

练习题&#xff1a; 图所示为一个问卷调查网页&#xff0c;请制作出来。要求&#xff1a;大标题用h1标签&#xff1b;小题目用h3标签&#xff1b;前两个问题使用有序列表&#xff1b;最后一个问题使用无序列表。 代码&#xff1a; <!DOCTYPE html> <html> <he…

Java基础-内部类与异常处理

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、Java 内部类 什么是内部类&#xff1f; 使用内部类的优点 访问局部变量的限制 内部类和继承 内部…

C/C++中使用MYSQL

首先要保证下载好mysql的库和头文件&#xff0c;头文件在/usr/include/mysql/目录下&#xff0c;库在/usr/lib64/mysql/目录下&#xff1a; 一般情况下&#xff0c;在我们安装mysql的时候&#xff0c;这些都提前配置好了&#xff0c;如果没有就重装一下mysql。如果重装mysql还是…

华为ensp实验二--mux vlan的应用

一、实验内容 1.实验要求&#xff1a; 在交换机上创建三个vlan&#xff0c;vlan10、vlan20、vlan100&#xff0c;将vlan100设置为mux-vlan&#xff0c;将vlan10设置为group vlan&#xff0c;将vlan20设置为separate vlan&#xff1b;实现vlan10的设备在局域网内可以进行互通&…

Redis知识分享(三)

目录 前言 七、事务管理 7.1事务中的异常处理 八、订阅发布 8.1概述 ​8.2.Redis针对发布订阅相关指令 九、主从复制 9.1主从复制概述 9.2.主从复制的用处 9.3主从复制实现原理 ​9.3.1.psync指令 9.3.2.复制偏移量 9.3.3复制积压缓冲区&节点ID 前言 今天…

Java基础-组件及事件处理(中)

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 BorderLayout布局管理器 说明&#xff1a; 示例&#xff1a; FlowLayout布局管理器 说明&#xff1a; …

【论文阅读】主动推理:作为感知行为的理论

文章目录 主动推理&#xff1a;作为感知行为的理论摘要1.引言2. 主动推理的概念和历史根源3. 主动推理的规范视角—以及它的发展历程 未完待续 主动推理&#xff1a;作为感知行为的理论 Active inference as a theory of sentient behavior 摘要 这篇文章综述了主动推理的历…

HuggingFace:基于YOLOv8的人脸检测模型

个人操作经验总结 1、YOLO的环境配置 github 不论base环境版本如何&#xff0c;建议在conda的虚拟环境中安装 1.1、创建虚拟环境 conda create -n yolov8-face python3.9conda create &#xff1a;创建conda虚拟环境&#xff0c; -n &#xff1a;给虚拟环境命名的…

React--》如何高效管理前端环境变量:开发与生产环境配置详解

在前端开发中&#xff0c;如何让项目在不同环境下表现得更为灵活与高效&#xff0c;是每个开发者必须面对的挑战&#xff0c;从开发阶段的调试到生产环境的优化&#xff0c;环境变量配置无疑是其中的关键。 env配置文件&#xff1a;通常用于管理项目的环境变量&#xff0c;环境…

SpringSecurity+jwt+captcha登录认证授权总结

SpringSecurityjwtcaptcha登录认证授权总结 版本信息&#xff1a; springboot 3.2.0、springSecurity 6.2.0、mybatis-plus 3.5.5 认证授权思路和流程&#xff1a; 未携带token&#xff0c;访问登录接口&#xff1a; 1、用户登录携带账号密码 2、请求到达自定义Filter&am…

计算机视觉和机器人技术中的下一个标记预测与视频扩散相结合

一种新方法可以训练神经网络对损坏的数据进行分类&#xff0c;同时预测下一步操作。 它可以为机器人制定灵活的计划&#xff0c;生成高质量的视频&#xff0c;并帮助人工智能代理导航数字环境。 Diffusion Forcing 方法可以对嘈杂的数据进行分类&#xff0c;并可靠地预测任务的…

2024-11-17 -MATLAB三维绘图简单实例

1. x -1:0.05:1; y x; [X, Y] meshgrid(x, y); f (X, Y) (sin(pi * X) .* sin(pi * Y)) .^ 2.*sin(2.*X2.*Y); mesh(X, Y, f(X, Y)); % 调用函数f并传递X和Y xlabel(X-axis); ylabel(Y-axis); zlabel(Z-axis); title(Surface Plot of (sin(pi * X) .* sin(pi * Y)) .^ 2.*…

WebAssembly在桌面级应用开发中的探索与实践

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 WebAssembly在桌面级应用开发中的探索与实践 WebAssembly在桌面级应用开发中的探索与实践 WebAssembly在桌面级应用开发中的探索…

第二十一周学习周报

目录 摘要Abstract1. LSTM原理2. LSTM反向传播的数学推导3. LSTM模型训练实战总结 摘要 本周的学习内容是对LSTM相关内容的复习&#xff0c;LSTM被设计用来解决标准RNN在处理长序列数据时遇到的梯度消失和梯度爆炸问题。LSTM通过引入门控机制来控制信息的流动&#xff0c;从而…

《Spring 基础之 IoC 与 DI 入门指南》

一、IoC 与 DI 概念引入 Spring 的 IoC&#xff08;控制反转&#xff09;和 DI&#xff08;依赖注入&#xff09;在 Java 开发中扮演着至关重要的角色&#xff0c;是提升代码质量和可维护性的关键技术。 &#xff08;一&#xff09;IoC 的含义及作用 IoC 全称为 Inversion of…