一、目录结构
- assets放置了静态文件
- components放置了通用组件
- config放置了一些常量配置
- helper放置了主要的功能模块比如ajax.js、api.js
- mixins放置了混入
- router放置了路由文件
- store放置了vuex的文件
- views放置了页面文件
二、 鉴权
项目的登陆状态由Vuex+LocalStorage维持,项目的鉴权主要靠 路由鉴权 和 Ajax鉴权。
store结构
state,localStorage维持了登陆信息的持久化
const state = {
userInfo: $storage.getLocalStorage('userInfo') || {}
}
设置和删除userInfo的都放在mutation里
import { $storage } from '@/helper'
export default {
setUserInfo (state, info) {
state.userInfo = info
$storage.setLocalStorage('userInfo', info)
},
deleteUserInfo (state) {
state.userInfo = null
$storage.removeLocalStorage('userInfo')
}
}
向服务器获取userInfo放在action里(异步类型的)
import { $apis } from '@/helper'
export default {
async getUserInfo ({ commit, state }) {
let userId = state.userInfo.id
if (userId) {
$apis.getUserInfo(userId).then(res => {
commit('setUserInfo', res)
})
}
}
}
auth文件
实际上router鉴定权限调用的都是这两个方法,把它们分离出一个文件,用来检查有没有userInfo和userInfo是否是Admin的权限。
import { $apis, $storage } from '.'
export default {
checkSession () {
return $storage.getLocalStorage('userInfo') || false
},
async checkAdmin () {
let userInfo = $storage.getLocalStorage('userInfo') || {}
let userId = userInfo.id || 0
if (!userId) return false
userInfo = userInfo || (await $apis.getUserInfo(userId))
let isAdmin = userInfo.permission < 2
return userInfo.id && isAdmin
}
}
router结构
鉴权这里用的是全局前置守卫,所以在beforeEachHooks.js写上鉴定权限的规则。
规则:在需要admin权限的地方无权限访问,则会跳到404页面;在需要登陆权限的地方无权限访问,则会跳到login界面。
import { $auth } from '@/helper'
export default {
checkVisitAuth (to, from, next) {
if (to.meta.isNeedAdmin) {
$auth.checkAdmin().then(result => {
return result ? next() : next({ path: '/404' })
})
} else if (to.meta.isNotNeedLogin) {
next()
} else {
$auth.checkSession() ? next() : next({ path: '/login' })
}
}
}
在index.js处挂载该规则
Object.values(beforeEachHooks).forEach(hook => {
routerInstance.beforeEach(hook)
})
Ajax
用了一层requestHandle
来封装 axios,以后可能会改用interceptors
来定制,比较符合通用的做法。
后端返回 401 就是没有登陆或者登陆信息过期,这时候清空登陆缓存状态,然后重定向到登陆页面。
后端返回 403 就是没有权限访问,由于本项目403也是跳到404页面所以和404同用一个处理方式。
后端返回 415 是访问api过多被限制。
/**
* @param url
* @param method get|post|put|delete...
* @param params like queryString. if a url is index?a=1&b=2, params = {a: '1', b: '2'}
* @param data post data, use for method put|post
* @returns {Promise}
*/
function requestHandle (url, method, options) {
if (options !== undefined) {
var { params = {}, data = {} } = options
} else {
options = {}
}
return new Promise((resolve, reject) => {
axios({
url,
method,
params,
data
})
.then(
res => {
if (res.status === 200 || res.status === 201 || res.status === 204) {
resolve(res.data)
} else {
reject(res)
}
},
err => {
if (err.response.status === 401) {
store.commit('deleteUserInfo')
Vue.prototype.$error('请登陆')
window.location.href = '/login'
reject(err)
} else if (
err.response.status === 403 ||
err.response.status === 404
) {
reject(err.response.data)
} else if (err.response.status === 415) {
Vue.prototype.$error('您访问太过频繁, 已被限速')
reject(err.response.data)
}
}
)
.catch(err => {
Vue.prototype.$error(err)
reject(err)
})
})
}
总体结构
实际上该项目的鉴权就是路由拦截那些正常的权限缺失,减少后端压力,而在登陆信息过期或者恶意修改登陆信息的会遭到后端的铁拳制裁😄,后端必须要有完整的鉴权系统才能是健壮的。