vue-element-admin 个人理解登录流程
登录流程
1.页面发起登录请求
一、页面发起登录请求,会调用vuex中的 login() 方法(@/store/modules/user.js)
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { result } = response
// 设置 token,作为用户已登陆的前端标识,存在 cookie 中
commit('SET_TOKEN', result.token)
setToken(result.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
二 、全局拦截请求和响应 [有白名单] 和响应(位置:@/utils/request.js)
判断状态码code是否正确
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// 创建一个AXIOS实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // 跨域请求时发送cookies
timeout: 5000 // 请求超时
})
// request interceptor 请求拦截器
service.interceptors.request.use(
config => {
// 在发出请求前做的
if (store.getters.token) {
// 让每个请求携带 token
// ['X-Token'] 是自定义头密钥
// 请根据实际情况修改
//const token = sessionStorage.getItem('X-Token')
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// 处理请求错误
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor 响应拦截器
service.interceptors.response.use(
/**
* 如果您想要获取 headers或状态之类的http信息
* Please return response => response
*/
/**
* 通过自定义代码确定请求状态
* 这只是个例子
* 您还可以通过http状态代码判断状态
*/
response => {
const res = response.data
// 如果自定义代码不是20000,则判断为错误。
if (res.code !== '000000') {
Message({
message: res.message || 'Error',
type: 'error,code值不匹配',
duration: 5 * 1000
})
// 50008: 非法token 50012: 其他客户端登录 50014: Token 过期
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
// alert("获取登录数据成功!")
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
三、若果正确,继续返回 第一步 ,login()方法,例如,将token存入cookie和vuex中
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { result } = response
// 设置 token,作为用户已登陆的前端标识,存在 cookie 中
commit('SET_TOKEN', result.token)
setToken(result.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
四、登录步骤已经走完,现在需要路由跳转到首页,跳转前需要先全局判断是否有token存在(位置:@/permission.js)先用getToken函数读取token如果有,先判断是不是要跳转到login页面,如果是,就直接重定向到首页,如果不是则需要判断vuex里是否存在用户的信息,如果用户信息为空,则需要根据用户token重新获取
const hasToken = getToken()
// alert('thken')
console.log(hasToken)
if (hasToken) {
window.console.log('有token, goto path is ' + to.path)
if (to.path === '/login') {
// 如果已登录,请重定向到主页
window.console.log('jumper home /')
next({ path: '/' })
NProgress.done()
} else {
// 判断store里是否存在有角色信息
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// store角色信息为空,即重新获取
window.console.log('角色为空,并获取用户信息...')
// 获取用户信息
//注意:角色必须是一个对象数组!例如:['admin']或,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
window.console.log(store.getters.roles)
// 根据角色生成可访问路由映射
window.console.log('get permission routes map...')
// alert('到这来')
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// 动态添加可访问路由
window.console.log('add routes......')
window.console.log(accessRoutes)
window.console.log('add routes......')
router.addRoutes(accessRoutes)
// 破解方法,以确保addRoutes是完整的
// 设置replace: true,这样导航就不会留下历史记录
next({ ...to, replace: true })
} catch (error) {
// 移除token并转到登录页以重新登录
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
***五***、 如果没有用户信息,就会调用vuex中(@/store/modules/user.js)中的getInfo()方法来获取用户信息
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
console.log('getInfo方法state.token');
console.log(state.token)
getInfo(state.token).then(response => {
const { result } = response
console.log('获取用户信息的result')
console.log(result)
if (!result) {
reject('Verification failed, please Login again.')
}
const { roles, name, avatar, introduction } = result
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: 角色必须是非空数组!')
}
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_INTRODUCTION', introduction)
resolve(result)
}).catch(error => {
reject(error)
})
})
},
六、vuex中的getInfo()方法中又调用了(@/api/user.js)中的getInfo()方法并传入了用户的token标识
export function getInfo(token) {
return request({
url: '/user/info',
method: 'get',
withCredentials: true,
params: { token }
})
}
七、在mock(位置:mock/user.js)中定义接口返回的数据,返回数据一定要有roles权限,且是数组
const users = {
'admin-token': {
roles: ['admin'],
introduction: 'I am a super administrator',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: 'Super Admin'
},
'editor-token': {
roles: ['editor'],
introduction: 'I am an editor',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: 'Normal Editor'
}
}
//此时传入的token为admin-token所以返回的数据info为users['admin-token']
// get user info
{
url: '/user/info\.*',
type: 'get',
response: config => {
console.log('获取用户信息')
const { token } = config.query
const info = users[token]
// mock error
if (!info) {
return {
code: 50008,
message: 'Login failed, unable to get user details.'
}
}
return {
code: '000000',
result:info
}
}
},
八、然后数据请求成功,重新返回***五***、vuex中(@/store/modules/user.js)中的getInfo()方法成功来继续下一步操作将这些数据存入vuex
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
console.log('getInfo方法state.token');
console.log(state.token)
getInfo(state.token).then(response => {
const { result } = response
console.log('获取用户信息的result')
console.log(result)
if (!result) {
reject('Verification failed, please Login again.')
}
const { roles, name, avatar, introduction } = result
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: 角色必须是非空数组!')
}
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_INTRODUCTION', introduction)
resolve(result)
}).catch(error => {
reject(error)
})
})
},
九、此时获取用户token已经成功,拿用户token来获取用户信息也成功,所以现在走进 四 、顺着方法的执行顺序继续往下执行,发现又调用了一个方法(位置:@/store/modules/permission.js)下的generateRoutes方法
第四步代码
const hasToken = getToken()
// alert('thken')
console.log(hasToken)
if (hasToken) {
window.console.log('有token, goto path is ' + to.path)
if (to.path === '/login') {
// 如果已登录,请重定向到主页
window.console.log('jumper home /')
next({ path: '/' })
NProgress.done()
} else {
// 判断store里是否存在有角色信息
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// store角色信息为空,即重新获取
window.console.log('角色为空,并获取用户信息...')
// 获取用户信息
//注意:角色必须是一个对象数组!例如:['admin']或,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
window.console.log(store.getters.roles)
// 根据角色生成可访问路由映射
window.console.log('get permission routes map...')
// alert('到这来')
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// 动态添加可访问路由
window.console.log('add routes......')
window.console.log(accessRoutes)
window.console.log('add routes......')
router.addRoutes(accessRoutes)
// 破解方法,以确保addRoutes是完整的
// 设置replace: true,这样导航就不会留下历史记录
next({ ...to, replace: true })
} catch (error) {
// 移除token并转到登录页以重新登录
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
本次走到
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
这个地方
十、这个方法的目的就是通过请求后端数据获取异步路由,拿着用户的roles权限数组去遍历和整合一个全新的侧边栏
虽然这里我自定义了一个函数getAuthMenu模拟向mock发送数据,但返回的结果并不是异步路由的列表,只是一个为000000的状态码,只是一个模拟,也可以像原本的那样,不要
// 根据用户权限异步生成全新路由
generateRoutes({ commit }, roles) {
// alert("store")
return new Promise(resolve => {
// 先查询后台并返回左侧菜单数据并把数据添加到路由
getAuthMenu(state.token).then(response => {
// let data = response
console.log(response)
if (response.code !== '000000') { //请求失败
console.log(1)
} else {
// 模拟加载成功,返回的路由
let accessedRoutes
console.log('判断侧边栏异步加载权限的roles')
console.log(roles)
if (roles.includes('admin')) { //如果roles权限数组里包含admin则将获取整个异步路由的权限,当没有异步路由时,就定义为一个空数组
accessedRoutes = asyncRoutes || []
} else { //如果roles不包含admin就说明这个用户不是最高权限,需要过滤掉他没有权限的路由
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) //这里类似过滤器
}
commit('SET_ROUTES', accessedRoutes) //将整理好的异步路由放入vuex
resolve(accessedRoutes) //将整理好的异步路由返回给调用者
}
})
})
}
十一、异步路由已经整合过滤完成,下面就是合并路由了,看第 *** 四 *** 步的下一步会发现现在进入了
第四步代码
const hasToken = getToken()
// alert('thken')
console.log(hasToken)
if (hasToken) {
window.console.log('有token, goto path is ' + to.path)
if (to.path === '/login') {
// 如果已登录,请重定向到主页
window.console.log('jumper home /')
next({ path: '/' })
NProgress.done()
} else {
// 判断store里是否存在有角色信息
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// store角色信息为空,即重新获取
window.console.log('角色为空,并获取用户信息...')
// 获取用户信息
//注意:角色必须是一个对象数组!例如:['admin']或,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
window.console.log(store.getters.roles)
// 根据角色生成可访问路由映射
window.console.log('get permission routes map...')
// alert('到这来')
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// 动态添加可访问路由
window.console.log('add routes......')
window.console.log(accessRoutes)
window.console.log('add routes......')
router.addRoutes(accessRoutes)
// 破解方法,以确保addRoutes是完整的
// 设置replace: true,这样导航就不会留下历史记录
next({ ...to, replace: true })
} catch (error) {
// 移除token并转到登录页以重新登录
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
现在走到了
router.addRoutes(accessRoutes)
这是vue-router的一个方法,把本地原本的路由和异步获取的路由合并成一个路由
更多推荐
所有评论(0)