从零构建vue3项目(二)
Vue3项目增强配置:Axios封装、鉴权与代码扫描
1. Axios二次封装与拦截器配置
安装Axios
npm install axios
创建Axios实例 src/utils/request.js
import axios from 'axios'
import { useUserStore } from '@/stores/user'
import router from '@/router'// 创建axios实例
const service = axios.create({baseURL: import.meta.env.VITE_API_BASE_URL,timeout: 10000,headers: {'Content-Type': 'application/json;charset=utf-8'}
})// 请求拦截器
service.interceptors.request.use((config) => {const userStore = useUserStore()// 如果token存在,添加到请求头if (userStore.token) {config.headers.Authorization = `Bearer ${userStore.token}`}// 在这里可以添加全局参数if (config.method === 'get') {config.params = {...config.params,_t: Date.now() // 防止缓存}}return config},(error) => {return Promise.reject(error)}
)// 响应拦截器
service.interceptors.response.use((response) => {const res = response.data// 自定义状态码处理if (res.code !== 200) {// token过期if (res.code === 401) {const userStore = useUserStore()userStore.logout()router.push('/login?redirect=' + encodeURIComponent(router.currentRoute.value.fullPath))return Promise.reject(new Error(res.message || '登录状态已过期'))}// 其他错误return Promise.reject(new Error(res.message || 'Error'))} else {return res}},(error) => {// HTTP状态码处理if (error.response) {switch (error.response.status) {case 400:error.message = '请求错误'breakcase 401:error.message = '未授权,请重新登录'const userStore = useUserStore()userStore.logout()router.push('/login')breakcase 403:error.message = '拒绝访问'breakcase 404:error.message = '请求地址出错'breakcase 500:error.message = '服务器内部错误'breakdefault:error.message = `连接错误 ${error.response.status}`}} else if (error.request) {error.message = '服务器未响应'} else {error.message = '网络错误'}// 统一错误提示ElMessage.error(error.message)return Promise.reject(error)}
)export default service
使用示例 src/api/user.js
import request from '@/utils/request'export function login(data) {return request({url: '/auth/login',method: 'post',data})
}export function getUserInfo() {return request({url: '/user/info',method: 'get'})
}
2. Token鉴权集成
Pinia用户状态管理 src/stores/user.js
import { defineStore } from 'pinia'
import { login, logout, getUserInfo } from '@/api/user'
import { resetRouter } from '@/router'export const useUserStore = defineStore('user', {state: () => ({token: localStorage.getItem('token') || '',name: '',avatar: '',roles: []}),actions: {// 登录async login(userInfo) {const { username, password } = userInfotry {const res = await login({ username, password })this.token = res.tokenlocalStorage.setItem('token', res.token)return this.getInfo()} catch (error) {return Promise.reject(error)}},// 获取用户信息async getInfo() {try {const res = await getUserInfo()const { name, avatar, roles } = resthis.name = namethis.avatar = avatarthis.roles = rolesreturn res} catch (error) {return Promise.reject(error)}},// 登出async logout() {try {await logout()} finally {this.token = ''this.roles = []localStorage.removeItem('token')resetRouter()}}}
})
路由守卫 src/router/permission.js
import router from './index'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'const whiteList = ['/login'] // 不重定向白名单router.beforeEach(async (to, from, next) => {const userStore = useUserStore()// 确定用户是否已登录if (userStore.token) {if (to.path === '/login') {next({ path: '/' })} else {// 检查用户是否已获取角色信息if (userStore.roles.length === 0) {try {await userStore.getInfo()// 动态生成可访问路由const accessRoutes = await generateRoutes(userStore.roles)accessRoutes.forEach(route => {router.addRoute(route)})next({ ...to, replace: true })} catch (error) {// 获取用户信息失败,重定向到登录页userStore.logout()ElMessage.error(error || '验证失败,请重新登录')next(`/login?redirect=${to.path}`)}} else {next()}}} else {if (whiteList.includes(to.path)) {next()} else {next(`/login?redirect=${to.path}`)}}
})
3. 代码扫描工具集成
SonarQube配置 (可选)
.sonarcloud.properties
:
sonar.projectKey=my-vue3-app
sonar.organization=your-org
sonar.host.url=https://sonarcloud.io
sonar.login=your-sonar-tokensonar.sources=src
sonar.tests=src
sonar.test.inclusions=**/*.spec.js,**/*.test.js
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.exclusions=**/node_modules/**,**/dist/**,**/coverage/**,**/public/**,**/*.config.js
添加扫描脚本 package.json
{"scripts": {"sonar": "sonar-scanner","test:coverage": "vitest run --coverage","lint:ci": "eslint . --ext .vue,.js,.jsx,.ts,.tsx --format json --output-file eslint-report.json"}
}
GitHub Action集成 .github/workflows/code-analysis.yml
name: Code Analysis
on:push:branches: [ main ]pull_request:branches: [ main ]jobs:analysis:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Set up Node.jsuses: actions/setup-node@v2with:node-version: '16'- name: Install dependenciesrun: npm install- name: Run tests with coveragerun: npm run test:coverage- name: Run linterrun: npm run lint:ci- name: SonarCloud Scanuses: SonarSource/sonarcloud-github-action@v1env:GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
4. 安全增强配置
CSP配置 vite.config.js
import { defineConfig } from 'vite'export default defineConfig({// ...其他配置server: {headers: {'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"}}
})
Helmet中间件 (Node.js后端)
const helmet = require('helmet')app.use(helmet({contentSecurityPolicy: {directives: {defaultSrc: ["'self'"],scriptSrc: ["'self'", "'unsafe-inline'"],styleSrc: ["'self'", "'unsafe-inline'"],imgSrc: ["'self'", "data:"]}},hsts: {maxAge: 63072000,includeSubDomains: true,preload: true}
}))
5. 完整增强后的项目结构
my-vue3-app/
├── src/
│ ├── api/ # API接口
│ │ ├── user.js # 用户相关API
│ │ └── ... # 其他模块API
│ ├── router/
│ │ ├── index.js # 基础路由
│ │ ├── permission.js # 路由守卫
│ │ └── modules/ # 动态路由模块
│ ├── stores/
│ │ ├── index.js # Pinia主文件
│ │ ├── user.js # 用户状态
│ │ └── ... # 其他状态模块
│ ├── utils/
│ │ ├── request.js # Axios封装
│ │ └── auth.js # 鉴权工具
├── .sonarcloud.properties # SonarQube配置
├── .github/workflows/ # CI/CD配置
└── ... # 其他原有文件
6. 关键点说明
-
Axios封装特点:
- 统一错误处理机制
- 自动Token注入
- 防止GET请求缓存
- 响应数据统一格式处理
-
鉴权系统特点:
- JWT Token存储与自动更新
- 动态路由权限控制
- 路由守卫保护
- 角色基础权限系统
-
代码扫描集成:
- 本地SonarQube扫描支持
- CI/CD流水线集成
- 测试覆盖率报告
- 静态代码分析
-
安全增强:
- CSP内容安全策略
- HTTPS严格传输安全
- XSS防护头
- CSRF令牌支持(后端需配合)
这套配置提供了企业级Vue3应用所需的核心安全、鉴权和代码质量保障功能,可以根据项目实际需求进行适当调整。