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應用所需的核心安全、鑒權和代碼質量保障功能,可以根據項目實際需求進行適當調整。