示例成品 · 平台演示,按左边这组点选真跑出来的
直接落地三块:Mock 接口、axios 拦截器、路由守卫。
假设你用的是 Vue3 + axios + Vue Router4,Mock.js 只拦 XHR,axios 默认走 XHR,可以直接用。
---
## 1. Mock 文件:`src/mock/auth.js`
```js
import Mock from 'mockjs'
// ========== 假账号,固定几条 ==========
const USERS = {
admin: {
password: '123456',
userId: 1,
username: 'admin',
roles: ['admin'],
permissions: ['*']
},
editor: {
password: '123456',
userId: 2,
username: 'editor',
roles: ['editor'],
permissions: ['article:read', 'article:write']
},
viewer: {
password: '123456',
userId: 3,
username: 'viewer',
roles: ['viewer'],
permissions: ['article:read']
}
}
// ========== 响应结构契约 ==========
// 统一:{ code, message, data }
// code: 0 成功;401 未登录/过期;其他业务错误
// accessToken 10 秒过期,方便你测刷新
const ACCESS_TOKEN_EXPIRE = 10
const REFRESH_TOKEN_EXPIRE = 60 * 60 // 1 小时
const accessTokenStore = new Map()
const refreshTokenStore = new Map()
function ok(data = null, message = 'ok') {
return { code: 0, message, data }
}
function fail(code = 500, message = 'error') {
return { code, message, data: null }
}
function createToken(prefix) {
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`
}
function userInfo(user) {
return {
userId: user.userId,
username: user.username,
roles: user.roles,
permissions: user.permissions
}
}
function genAccessToken(user) {
const token = createToken('mock-access')
accessTokenStore.set(token, {
userInfo: userInfo(user),
expiresAt: Date.now() + ACCESS_TOKEN_EXPIRE * 1000
})
return { token, expiresIn: ACCESS_TOKEN_EXPIRE }
}
function genRefreshToken(user) {
const token = createToken('mock-refresh')
refreshTokenStore.set(token, {
userInfo: userInfo(user),
expiresAt: Date.now() + REFRESH_TOKEN_EXPIRE * 1000
})
return { token, expiresIn: REFRESH_TOKEN_EXPIRE }
}
function getTokenFromReq(options) {
const headers = options.headers || {}
const auth = headers.Authorization || headers.authorization || ''
return auth.replace('Bearer ', '')
}
function getByAccessToken(token) {
const record = accessTokenStore.get(token)
if (!record) return null
if (Date.now() > record.expiresAt) {
accessTokenStore.delete(token)
return { expired: true }
}
return { userInfo: record.userInfo }
}
function getByRefreshToken(token) {
const record = refreshTokenStore.get(token)
if (!record) return null
if (Date.now() > record.expiresAt) {
refreshTokenStore.delete(token)
return { expired: true }
}
return { userInfo: record.userInfo }
}
// ========== 登录接口 ==========
Mock.mock(/\/api\/auth\/login/, 'post', (options) => {
let body = {}
try {
body = JSON.parse(options.body || '{}')
} catch (e) {}
const user = USERS[body.username]
if (!user || user.password !== body.password) {
return fail(401, '用户名或密码错误')
}
const access = genAccessToken(user)
const refresh = genRefreshToken(user)
return ok({
accessToken: access.token,
expiresIn: access.expiresIn,
refreshToken: refresh.token,
userInfo: userInfo(user)
})
})
// ========== 查询当前用户 ==========
Mock.mock(/\/api\/auth\/me/, 'get', (options) => {
const token = getTokenFromReq(options)
if (!token) return fail(401, '未登录')
const res = getByAccessToken(token)
if (!res) return fail(401, 'token 无效')
if (res.expired) return fail(401, 'token 已过期')
return ok(res.userInfo)
})
// ========== 刷新 token ==========
Mock.mock(/\/api\/auth\/refresh/, 'post', (options) => {
let bo
点左边「开工 · 直接出成品」,出一份你自己的版本(文字免费)