准备工作

修改 vue.config.js

proxy: {
      // change xxx-api/login => mock/login
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      // 为了不影响其他请求的使用,可以将这里注释掉
      /* [process.env.VUE_APP_BASE_API]: {
        target: `http://localhost:9000`,
        changeOrigin: true,
        pathRewrite: {
          ['^' + process.env.VUE_APP_BASE_API]: ''
        }
      },*/
      // 配置自己的代理,userApi 用于用户登录生成token
      '/userApi': {
        target: 'http://localhost:9000',
        changeOrigin: true,
        pathRewrite: {
          '^/userApi': ''
        }
      },
      // tokenApi 用于用户访问其他模块时,token 验证
      '/tokenApi': {
        target: 'http://localhost:9000',
        changeOrigin: true,
        pathRewrite: {
          '^/tokenApi': ''
        }
      },
    },
    // 将这里注释掉,因为我们要访问自己的登录接口,而不是mock里面的登录接口
    // after: require('./mock/mock-server.js')

修改 .env.development


```javascript
# base api 
// 去掉 dev-api
VUE_APP_BASE_API = ''

修改 src/utils/request.js (修改 response 的返回码!!很重要)

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
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
 
  response => {
    const res = response.data
    // if the custom code is not 20000, it is judged as an error.
    // !!! 这里一定要修改成与后端返回一致的返回码, vue-admin-template的默认访问成功的返回码是20000
    if (res.code !== 200) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      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 {
      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

修改Vue-admin-template的默认登录接口

src\views\login\index.vue

this.$refs.loginForm.validate(valid => {
   if (valid) {
	    this.loading = true
	    this.$store.dispatch('user/login', this.loginForm).then(() => {
	    this.$router.push({path: this.redirect || '/'})
	    this.loading = false
    }).catch(() => {
      	this.loading = false
    })
   } else {
        return false
   }
})

通过 this.$store.dispatch 可以知道调用了 store 里面的 user/login 方法

src/store/modules/user.js

import { login, logout, getInfo } from '@/api/user'
login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      // 调用 @/api/user 下的 login 方法,该方法里面包含了自己要修改的登录接口(拿到token 方便用户后续的访问)
      login({ username: username.trim(), password: password }).then(response => {
        const token = JSON.parse(response.data).token
        commit('SET_TOKEN', token)
        setToken(token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  }

src/api/user.js

// 一个request请求,访问自己写的登录接口
export function login(data) {
  return request({
    url: '/userApi/user',
    method: 'post',
    data: Qs.stringify(data),
    params:{
      methodName:"login"
    }
  })
}
后端部分

登录令牌类

@Data
public class LoginToken {
    private String token;
}

servlet 处理

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
      
        // 用户登录
        if (UserMethod.LOGIN.equals(methodName)) {
            // 用户登录逻辑省略...
            LoginToken loginToken = new LoginToken();
            // 在这里设置用户将要携带的token
            loginToken.setToken("user-token");
            response.getWriter().append(JSON.toJSONString(new ResponseResult(true, 200, "登录成功", JSON.toJSONString(loginToken))));
        }
        
        // 用户注销
        if (UserMethod.LOGOUT.equals(methodName)) {
            response.getWriter().append(JSON.toJSONString(new ResponseResult(true, 200, "退出成功")));
        }
    }
}

登录角色类

@Data
public class LoginRole {
    private String role;
    private String introduction;
    private String avatar;
    private String name;
}

令牌验证servlet

@WebServlet("/token")
public class TokenServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        LoginRole loginRole = new LoginRole();
        // 获取登录时的发放的token
        String token = request.getParameter("token");
        // 验证token
        if (Token.USER_TOKEN.equals(token)){
            // 设置角色类型
            loginRole.setRole("user");
			//设置角色头像
            loginRole.setAvatar("https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
            // 设置角色介绍
            loginRole.setIntroduction("I am an user");
            // 设置角色名字
            loginRole.setName("User");
            response.getWriter().append(JSON.toJSONString(new ResponseResult(true, 200, "获取token成功", JSON.toJSONString(loginRole))));
        }
    }
}

继续分析 src/store/modules/user.js

const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password }).then(response => {
        // 经过上面的代码 已经完成了用户的登录,返回的是
        //{
        //	code:200,
        //  message: “登录成功”,
        //  success: true,
        //  data: "{"token": "user-token"}"
        //}
        // 对返回的结果进行JSON解析成对象,并取出里面的token令牌
        const token = JSON.parse(response.data).token
        // 利用store设置cookie
        commit('SET_TOKEN', token)
        // 调用设置token的方法,下面进入该方法
        setToken(token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

src/utils/auth.js

import Cookies from 'js-cookie'
// 在这里设置,用户访问其他模块时使用的cookie
// 为了省事 我将这里的key设置的和value一样
// TokenKey = 'user-token' -> cookie('user-token', 'user-token')
const TokenKey = 'user-token'
export function getToken() {
  return Cookies.get(TokenKey)
}
export function setToken(token) {
  return Cookies.set(TokenKey, token)
}
export function removeToken() {
  return Cookies.remove(TokenKey)
}

此时就已经走完了所有的登录流程
我们回到最开始的调用登录接口的登录页面中

this.$refs.loginForm.validate(valid => {
   if (valid) {
	    this.loading = true
	    this.$store.dispatch('user/login', this.loginForm).then(() => {
	    // 我们已经完成了所有的登录流程,并成功返回
	    // 接下来修改路由,去到首页
	    // 此时我们地址栏的路由是 http://localhost:9530/#/login?redirect=%2Fdashboard
	    // this.redirect = dashboard
	    // 即跳转到 dashboard 路由
	    this.$router.push({path: this.redirect || '/'})
	    this.loading = false
    }).catch(() => {
      	this.loading = false
    })
   } else {
        return false
   }
})

Vue-admin-template 每次访问路由时会去访问权限配置文件(permission.js)

src/permission.js

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

// 白名单,不需要登录信息就可以直接访问
const whiteList = ['/login'] // no redirect whitelist

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
  // 通过 auth.js 中的 getToken 取得登录时发放的 token 信息
  const hasToken = getToken()
  
  // 如果取到了令牌则说明用户已经登录
  if (hasToken) {
  	// 如果用户访问的还是登录页面,则直接跳转到首页
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      // 如果有用户的信息,则对将要访问的路由放行
      if (hasGetUserInfo) {
        next()
      } else {
      	// 否则要获取用户的信息
        try {
          // get user info
          // 调用 store 里面的 user.js,接下来分析 user.js 里面的 getInfo 方法
          await store.dispatch('user/getInfo')

          next()
        } 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()
    }
  }
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      // 调用 src/api/user.js 里面的 getInfo 方法
      getInfo(state.token).then(response => {
        // 通过上面的令牌验证 servlet,得到返回值
  		//{
  		//  code : 200,
  		//  message: "获取token成功",
  		//  success: true,
  		//  data: "{
  		//		"role" : "user",
  		//		"avatar": "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
  		//		"introduction":"I am an user",
  		//		"name" : "User"
  		//	}"
  		//}
  		// 对 data 进行解析
        let role = JSON.parse(response.data);
        const { data } = response
        if (!data) {
          reject('Verification failed, please Login again.')
        }
        // 存放用户的名字
        commit('SET_NAME', role.name)
        // 存放用户的头像
        commit('SET_AVATAR', role.avatar)
        resolve(role)
      }).catch(error => {
        reject(error)
      })
    })
  },

实现的效果如下图所示,右上角的是 avatar
在这里插入图片描述

src/api/user.js

// 根据令牌获得登录用户的信息
export function getInfo(token) {
  return request({
    url: '/tokenApi/token',
    method: 'get',
    params: { token }
  })
}

至此就完成了所有的登录以及访问其他模块的流程,最后就是用户的登出
在这里插入图片描述
点击 Log Out

来到 src/store/modules/user.js

logout({ commit, state }) {
    return new Promise((resolve, reject) => {
      // 调用 src/api/user.js 中的 logout 方法
      logout(state.token).then(() => {
        // 调用完后端的登出方法后的返回值
        // {
        //	  code:200,
        //	  message:"退出成功",
        //    success: true	 	
        // }
        // 将cookie里面的 'user-token' 对应的值设置为 '',相当于清除cookie 缓存
        commit('SET_TOKEN', '')
        removeToken()
        // 重置路由,将路由设置为登录的路由
        resetRouter()
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },
// 访问到上面后端部分的登出方法
export function logout() {
  return request({
    url: '/userApi/user',
    method: 'post',
    params:{
      methodName:'logout'
    }
  })
}

至此就完成了 vue-admin-template 登录接口的修改,实现了登录、用户信息验证、登出的功能

GitHub 加速计划 / vu / vue-admin-template
19.82 K
7.39 K
下载
PanJiaChen/vue-admin-template: 基于 Vue.js 和 Element UI 的后台管理系统模板,支持多语言、主题和布局切换。该项目提供了一个完整的后台管理系统模板,可以方便地实现后台管理系统的快速搭建和定制,同时支持多种数据源和插件扩展。
最近提交(Master分支:24 天前 )
4c18a3f4 - 2 年前
714ded11 - 3 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐