1. 登录页
- 文件目录 src/view/login/index.vue
- 登录方法
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
// 调用的是 store/modules/user.js 里的 login 方法
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
}
2. 登录过程
(1) 首先会执行 user.js 里的 login 方法,
- user.js 在 src/store/moudules/user.js
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
// 这里执行的 login 方法是 api/user.js 中的方法
login({ username: username.trim(), password: password }).then(response => {
//****这一部分改成你自己的业务逻辑****//
const { data } = response
//********************************//
commit('SET_TOKEN', data)
setToken(data)
resolve()
}).catch(error => {
reject(error)
})
})
}
(2) 修改 api/user.js 中的 login 方法
// 修改为你自己的后台地址,可能会产生跨域问题,百度有很多解决方案
// 如果你后台是 spring 项目,可以直接在相应的 controller加 @CrossOrigin 注解即可
export function login(data) {
return request({
url: 'http://localhost:8800/login',
method: 'post',
data: data
})
}
- 登陆后,token 存储到 cookie,以后发送请求会自动在请求头中添加 token 如果要修改相关信息,在 src/utils/request.js 中修改,包括 定义请求成功的返回码,token 相关的返回码
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// 请求时间超过 5s 视为错误
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// 请求前处理
if (store.getters.token) {
// 为请求头添加 token
config.headers['RB-Token'] = getToken()
}
return config
},
error => {
// 错误处理
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* 如果您想获取http信息(例如标题或状态)
* 请返回响应=> 响应
*/
/**
* 通过自定义代码确定请求状态
* 这只是一个例子
* 您也可以通过HTTP状态代码来判断状态
*/
response => {
const res = response.data
// 如果自定义代码不是 1000,则将其判断为错误。
if (res.code !== 1000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 这里的返回码根据自己的业务逻辑进行调整,例如 token 过期
if (res.code === 2555 || res.code === 2111 || res.code === 2444) {
// to re-login
MessageBox.confirm('登录信息已过期,请重新登录', '确认', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
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
3. 授权过程
(1) 查看 permission.js 的执行流程
- 在登录完成后,执行了 setToken 将 token 信息保存到了 cookie 中(如果不想保存到 cookie 可以看后文的修改方法)。然后跳转到 '/' 路径,当路由发生变化时,触发了 permission.js 中的 beforeEach 方法。
- permission.js 目录 src/store/modules/permission.js
- 主要是看 router.beforeEach方法
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
console.log(hasToken);
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
// determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// 这里调用 store/modules/user.js 中的 info 方法
// 同样,这里的逻辑需要根据你自己的需要进行调整
//******************************************//
const res = await store.dispatch('user/getInfo')
const roles = res.roleArray;
//*******************************************//
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true })
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
(2) 获取用户信息
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
// 调用 api/user.js 中的 getInfo 方法
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
reject('验证失败,请重新登录')
}
// 获取用户的角色列表,用户名和头像
const roles = data.roleArray;
const name = data.username;
const avatar = data.avatar;
if (!roles || roles.length <= 0) {
reject('角色必须为一个非空数组')
}
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
resolve(data)
}).catch(error => {
reject(error)
})
})
4. 将 token 存储到 localStorage
const TokenKey = 'RB-Token'
export function getToken() {
return localStorage.getItem(TokenKey);
}
export function setToken(token) {
return localStorage.setItem(TokenKey, token);
}
export function removeToken() {
return localStorage.removeItem(TokenKey);
}