← 返回首页
vue-element-admin与Springboot整合(一)
发表时间:2023-03-19 23:36:34
vue-element-admin与Springboot整合

vue-element-admin 是一款基于vue和element-ui实现的开箱即用的后台管理系统。

vue-element-admin 是一款基于vue和element-ui实现的开箱即用的后台管理系统,本文探讨如何实现vue-element-admin与Springboot后端项目完美整合,实现真正的前后端分离的项目架构。

1.改造vue-element-admin登录流程为调用后端真实接口

vue-element-admin的登录流程默认是使用了mock模拟接口实现的。

1.实现后端接口

以国内比较流行sa-token权限框架实现为例,后端首先需要实现三个接口。分别是用户登录接口,获取用户信息接口和用户注销接口。

1).用户登录接口URL为:

http://localhost:8090/web-project/users/auth?username=管理员&password=123456

返回的json数据格式必须如下:

其中必须返回tokenName和tokenValue,实现前后端分离的登录权限认证。

{
    "data": {
        "createTime": "2023-03-13 14:00:45",
        "modifyTime": "2023-03-13 14:00:45",
        "flag": false,
        "version": 1,
        "uid": 1,
        "username": "管理员",
        "tokenName": "satoken",
        "tokenValue": "8fcf77ca-c52a-4785-a2f4-de62cd1536f2"      
    },
    "msg": "登录成功!",
    "code": 200
}

2).获取用户信息接口。

获取用户信息接口URL为:

http://localhost:8090/web-project/users/isLogin

返回的json数据格式必须如下:

该接口用于前端每次访问需要登录权限的资源时,都必须发送该请求以验证用户身份是否合法,返回状态码200表示该用户已经登录,并且返回该用户的角色信息(roles)。

{
    "data": {
        "createTime": "2023-03-13 14:00:45",
        "modifyTime": "2023-03-13 14:00:45",
        "flag": false,
        "version": 1,
        "uid": 1,
        "username": "管理员",
        "password": "",
        "avatar": "https://img.simoniu.com/vue-color-avatar11.png",
        "introduce": "我是管理员",
        "tokenName": "satoken",
        "tokenValue": "8fcf77ca-c52a-4785-a2f4-de62cd1536f2",
        "roles": [
            "admin"
        ]
    },
    "msg": "已登录",
    "code": 200
}

3).用户注销接口

用户注销接口URL:

http://localhost:8090/web-project/users/logout

返回的json数据格式必须如下:

{
    "data": null,
    "msg": "注销成功!",
    "code": 200
}

2.前端调用后端真实接口

这里需要修改vue-element-admin的四个js模块。分别是:

api/user.js 修改如下:

import request from '@/utils/request'


export function login(username, password) {
  return request({
    url: 'http://localhost:8090/web-project/users/auth?username=' + username + '&password=' + password,
    method: 'get'
  })
}


export function getInfo(token) {
  return request({
    url: 'http://localhost:8090/web-project/users/isLogin',
    method: 'get'
  })
}

export function logout() {
  return request({
    url: 'http://localhost:8090/web-project/users/logout',
    method: 'get'
  })
}

store/modules/user.js 修改如下:

import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter } from '@/router'

const state = {
  token: getToken(),
  name: '',
  avatar: '',
  introduction: '',
  roles: []
}

const mutations = {
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_INTRODUCTION: (state, introduction) => {
    state.introduction = introduction
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles
  }
}

const actions = {

  login({ commit }, userInfo) {
    console.log('----store/user/login-----------')
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login(username.trim(), password).then(response => {
        const { data } = response
        commit('SET_TOKEN', data.tokenValue)
        setToken(data.tokenValue)
        resolve()
      }).catch(error => {

        reject(error)
      })
    })
  },

  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const { data } = response

        if (!data) {
          reject('Verification failed, please Login again.')
        }

        const { roles, username, avatar } = data

        // roles must be a non-empty array
        if (!roles || roles.length <= 0) {
          reject('getInfo: roles must be a non-null array!')
        }

        commit('SET_ROLES', roles)
        commit('SET_NAME', username)
        commit('SET_AVATAR', avatar)
        // commit('SET_INTRODUCTION', introduction)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },

  // user logout
  logout({ commit, state, dispatch }) {
    return new Promise((resolve, reject) => {
      logout(state.token).then(() => {
        commit('SET_TOKEN', '')
        commit('SET_ROLES', [])
        removeToken()
        resetRouter()

        // reset visited views and cached views
        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
        dispatch('tagsView/delAllViews', null, { root: true })

        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // remove token
  resetToken({ commit }) {
    return new Promise(resolve => {
      commit('SET_TOKEN', '')
      commit('SET_ROLES', [])
      removeToken()
      resolve()
    })
  },

  // dynamically modify permissions
  async changeRoles({ commit, dispatch }, role) {
    const token = role + '-token'

    commit('SET_TOKEN', token)
    setToken(token)

    const { roles } = await dispatch('getInfo')

    resetRouter()

    // generate accessible routes map based on roles
    const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
    // dynamically add accessible routes
    router.addRoutes(accessRoutes)

    // reset visited views and cached views
    dispatch('tagsView/delAllViews', null, { root: true })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

utils/auth.js 修改如下:

import Cookies from 'js-cookie'

// const TokenKey = 'Admin-Token'
const TokenKey = 'satoken'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

utils/request.js 主要修改请求拦截器和响应状态码的值:


// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      //把satoken添加到请求头部 
      config.headers['satoken'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

...

 response => {
    const res = response.data
    // if the custom code is not 20000, it is judged as an error.
    //由于后端接口状态码200表示成功,这里把20000修改为200
    if (res.code !== 200) {
      Message({
        message: res.msg || 'Error',  //后端返回的消息属性也改为msg
        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.msg || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

至此,改造vue-element-admin登录流程为调用后端真实接口就真正实现了!