一, 路由拦截
登录拦截逻辑
第一步: 路由拦截
首先在定义路由的时候就需要多添加一个自定义字段 requireAuth, 用于判断该路由的访问是否需要登录. 如果用户已经登录, 则顺利进入路由, 否则就进入登录页面.
- const routes = [
- {
- path: '/',
- name: '/',
- component: Index
- },
- {
- path: '/repository',
- name: 'repository',
- meta: {requireAuth: true, // 添加该字段, 表示进入这个路由是需要登录的},
- component: Repository
- },
- {
- path: '/login',
- name: 'login',
- component: Login
- }
- ];
定义完路由后, 我们主要是利用 vue-router 提供的钩子函数 beforeEach() 对路由进行判断.
- router.beforeEach((to, from, next) => {
- if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
- if (store.state.token) { // 通过 vuex state 获取当前的 token 是否存在
- next();
- }
- else {
- next({
- path: '/login',
- query: {redirect: to.fullPath} // 将跳转的路由 path 作为参数, 登录成功后跳转到该路由
- })
- }
- }
- else {
- next();
- }
- })
每个钩子方法接收三个参数:
* to: Route: 即将要进入的目标 路由对象
* from: Route: 当前导航正要离开的路由
* next: Function: 一定要调用该方法来 resolve 这个钩子. 执行效果依赖 next 方法的调用参数.
* next(): 进行管道中的下一个钩子. 如果全部钩子执行完了, 则导航的状态就是 confirmed (确认的).
* next(false): 中断当前的导航. 如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮), 那么 URL 地址会重置到 from 路由对应的地址.
* next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址. 当前的导航被中断, 然后进行一个新的导航.
确保要调用 next 方法, 否则钩子就不会被 resolved.
完整的方法见 / src/router.js http://lib.csdn.net/base/javascript
其中, to.meta 中是我们自定义的数据, 其中就包括我们刚刚定义的 requireAuth 字段. 通过这个字段来判断该路由是否需要登录权限. 需要的话, 同时当前应用不存在 token, 则跳转到登录页面, 进行登录. 登录成功后跳转到目标路由.
登录拦截到这里就结束了吗? 并没有. 这种方式只是简单的前端路由控制, 并不能真正阻止用户访问需要登录权限的路由. 还有一种情况便是: 当前 token 失效了, 但是 token 依然保存在本地. 这时候你去访问需要登录权限的路由时, 实际上应该让用户重新登录.
这时候就需要结合 http 拦截器 + 后端接口返回的 http 状态码来判断.
第二步: 拦截器
要想统一处理所有 http 请求和响应, 就得用上 axios 的拦截器. 通过配置
http response inteceptor
, 当后端接口返回
401 Unauthorized(未授权)
, 让用户重新登录.
- // http request 拦截器
- axios.interceptors.request.use(
- config => {
- if (store.state.token) { // 判断是否存在 token, 如果存在的话, 则每个 http header 都加上 token
- config.headers.Authorization = `token ${store.state.token}`;
- }
- return config;
- },
- err => {
- return Promise.reject(err);
- });
- // http response 拦截器
- axios.interceptors.response.use(
- response => {
- return response;
- },
- error => {
- if (error.response) {
- switch (error.response.status) {
- case 401:
- // 返回 401 清除 token 信息并跳转到登录页面
- store.commit(types.LOGOUT);
- router.replace({
- path: 'login',
- query: {redirect: router.currentRoute.fullPath}
- })
- }
- }
- return Promise.reject(error.response.data) // 返回接口返回的错误信息
- });
二, http 拦截
拦截器
首先我们要明白设置拦截器的目的是什么, 当我们需要统一处理 http 请求和响应时我们通过设置拦截器处理方便很多.
这个项目我引入了 element ui 框架, 所以我是结合 element 中 loading 和 message 组件来处理的. 我们可以单独建立一个 http 的 js 文件处理 axios, 再到 main.js 中引入.
- /**
- * http 配置
- */
- // 引入 axios 以及 element ui 中的 loading 和 message 组件
- import axios from 'axios'
- import { Loading, Message } from 'element-ui'
- // 超时时间
- axios.defaults.timeout = 5000
- // http 请求拦截器
- var loadinginstace
- axios.interceptors.request.use(config => {
- // element ui Loading 方法
- loadinginstace = Loading.service({ fullscreen: true })
- return config
- }, error => {
- loadinginstace.close()
- Message.error({
- message: '加载超时'
- })
- return Promise.reject(error)
- })
- // http 响应拦截器
- axios.interceptors.response.use(data => {// 响应成功关闭 loading
- loadinginstace.close()
- return data
- }, error => {
- loadinginstace.close()
- Message.error({
- message: '加载失败'
- })
- return Promise.reject(error)
- })
- export default axios
这样我们就统一处理了 http 请求和响应的拦截. 当然我们可以根据具体的业务要求更改拦截中的处理.