0 the introduction

This article to prepare for the weekend, if not stepped on too many middle hole up and over the weekend should be issued, it is because too much on pit and himself is more difficult, insist on write a out of more than 99% of the Bug, can withstand the reader practice verification project of actual combat, drag to release today. The author has always insisted that the quality of the article is more important than quantity, the content of good enough articles will let more readers read.

The front end of this article is based on the open source project Vue-Element-Admin and the back end is based on the back end project BlogServer of the Vblog project. Why is there no vueblog front end in the Vblog project? Because many components in the Vueblog project did not exist, including Vuex, and the versions of many components were too low, the author found that the project could not be started after installing various required dependency packages, and errors were reported all the time, which could not be solved in a short time. I have previously cloned the source code of vue-element-admin project, which has most of the front-end components and dependency packages needed. The most important is the implementation of mock mock background data implementation of user login, dynamic loading of routing resources and initialization of menu list based on role control. We only need to modify the business requirements on the basis of this project. The author’s code implementation is presented below.

Nonsense not much to say, below begin to present content dry goods!

1 Back-end code modification

1.1 User Adding a Role

public class User implements UserDetails {
     // Current role
    private Role currentRole;
    
     public Role getCurrentRole(a) {
        return currentRole;
    }
​
    public void setCurrentRole(Role currentRole) {
        this.currentRole = currentRole;
    }
    
    // Other code omitted
}
Copy the code

1.2 The UserService#loadUserByUsername method was modified

UserService.java

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(username);
        if (user == null) {
            // Avoid returning null, which returns a User object with no value. This will fail later in the password comparison process
            return new User();
        }
        // Query the role information of the user and return it to the user
        List<Role> roles = rolesMapper.getRolesByUid(user.getId());
        // Roles with large permissions are ranked first
        roles.sort(Comparator.comparing(Role::getId));
        // The following two lines of code add the current role code
        user.setRoles(roles);
        user.setCurrentRole(roles.get(0));
        return user;
    }
Copy the code

1.3 Added the interface for querying route ID sets by Role ID

RouterResourceController.java

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(username);
        if (user == null) {
            // Avoid returning null, which returns a User object with no value. This will fail later in the password comparison process
            return new User();
        }
        // Query the role information of the user and return it to the user
        List<Role> roles = rolesMapper.getRolesByUid(user.getId());
        // Roles with large permissions are ranked first
        roles.sort(Comparator.comparing(Role::getId));
        // The following two lines of code add the current role code
        user.setRoles(roles);
        user.setCurrentRole(roles.get(0));
        return user;
    }
Copy the code

RoleRouterService.java

public List<String> queryCurrentRoleResourceIds(Integer roleId){
        List<Integer> resourceIds = roleRouterMapper.queryRouteResourceIdsByRoleId(roleId);
        List<String> resultList = new ArrayList<>();
        for(Integer resourceId: resourceIds){
            resultList.add(String.valueOf(resourceId));
        }
        return resultList;
    }
Copy the code

RoleRouterMapper.java

List<Integer> queryRouteResourceIdsByRoleId(Integer roleId);
Copy the code

RolesMapper.xml

<select id="queryRouteResourceIdsByRoleId" parameterType="Integer" resultType="Integer">
         select resource_id from role_resources
          where role_id=#{roleId,jdbcType=INTEGER}
</select>
Copy the code

Added cross-domain configuration to the 1.4 WebSecurityConfig class

@Override
    protected void configure(HttpSecurity http) throws Exception {
        // Configure cross-domain
        http.cors().configurationSource(corsConfigurationSource());
        
        // This method other code omitted, has been passed to personal Gitee code repository, interested partners can be cloned to view
        
    }

// Configure cross-domain access to resources
    private CorsConfigurationSource corsConfigurationSource(a) {
        CorsConfigurationSource source =   new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://localhost:3000");	* indicates that all requests are regarded as the same source. If you need to specify IP address and port number, you can change the IP address and port number to localhost: 8080, which are separated by commas (,).
        corsConfiguration.addAllowedHeader("*");//header, which headers are allowed? In this case, the token is used.
        corsConfiguration.addAllowedMethod("*");	// Allowed request methods, PSOT, GET etc
        corsConfiguration.setAllowCredentials(true); // Allow cookie authentication
        ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/ * *",corsConfiguration); // Configure the URL to allow cross-domain access
        return source;
    }
Copy the code

1.5 Data Preparation

(1) Execute the SQ in the router_resource_data. SQL script file in the SRC /main/resources directory of the BlogServer project by referring to the dynamic routing data in the index file under the SRC /router directory of the ue-element-admin project L Script adds a dynamic menu routing resource from the vue-element-admin project to the routing resource table.

(2) Execute role_resources_data. SQL in SRC /main/resources directory of blogServer project to allocate routing resources to role admin

(3) After the background service is started, three users are registered through the postman interface (because the user data is encrypted when the user login password is entered into the database, it is difficult to perform SQL addition, and the user registration logic happens to use Spring-Security to encrypt the user login password).

post http://localhost:8081/blog/user/reg
{
    "username": "sang"."nickname":"A little Rain in the South"."password": "sang123"."email": "[email protected]"
 }
Copy the code

Replace this with the following data in the request body to complete the user registration

{
    "username": "zhangsan"."nickname":"Zhang"."password": "zhangsan123"."email": "[email protected]"
  }
 {
    "username": "heshengfu"."nickname":"Alfred the Programmer."."password": "heshengfu123"."email": "[email protected]"
  }
Copy the code

The backend code for this article has been uploaded to my Gitee backend repository address: gitee.com/heshengfu12…

2.2 to modifysrc/utils/request.js

import axios from 'axios'
// import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'

axios.defaults.withCredentials = true
// 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= > {
    // 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
      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 (res.code ! == 20000) { Message({ message: res.message || 'Error', type: 'error', duration: 5 * 1000 }) if (res.code === 50008 || res.code === 50012 || res.code === 50014) { 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
Copy the code

The interception of the corresponding body of the interface needs to be commented here, because the status code of our background argument is not 2000 when it succeeds

2.3 to modifysrc/api/user.jsandsrc/api/role.jsTwo files

(1) user. Js

// Modify the login interface function
export function login(data) {
  return request({
    url: '/user/login'.method: 'post'.headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    data,
    transformRequest: [function(data) {
      // Do whatever you want to transform the data
      let ret = ' '
      for (const it in data) {
        ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
      }
      return ret
    }]
  })
}
Copy the code

Note: Any API of post request type must add headers and transformRequest, especially the callback function transformRequest for input parameters. If the headers and transformRequest are not added, the user name obtained by the background is always an empty string during login. Therefore, user authentication fails.

(2) role.js

Added interface function to query dynamic route set by role ID in role-. js file

export function getRouteIds(roleId) {
  return request({
    url: `/routerResource/currentRoleResourceIds? roleId=${roleId}`.method: 'get'.headers: {
      'Content-Type': 'application/json'}})}Copy the code

2.3 to modifysrc/store/modules/user.jsfile

import { login, logout } from '@/api/user'
import { getRouteIds, getRoutes } from '@/api/role'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'
import { Message } from 'element-ui'

const state = {
  token: getToken(),
  userBase: null.name: ' '.avatar: ' '.// introduction: '',
  roles: [].currentRole: null
}

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

const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) = > {
      login({ username: username, password: password }).then(response= > {
        if (response.status === 200 && response.data) {
          const data = response.data.userInfo
          const useBaseInfo = {
            username: data.username,
            nickname: data.nickname,
            email: data.email
          }
          window.sessionStorage.setItem('userInfo'.JSON.stringify(useBaseInfo))
          const { roles, currentRole } = data
          commit('SET_TOKEN', useBaseInfo)
          commit('SET_NAME', useBaseInfo.username)
          setToken(currentRole.id)
          commit('SET_ROLES', roles)
          window.sessionStorage.setItem('roles'.JSON.stringify(roles))
          commit('SET_CURRENT_ROLE', currentRole)
          window.sessionStorage.setItem('currentRole', currentRole)
          const avtar = '@/assets/avtars/avtar1.jpg'
          commit('SET_AVATAR', avtar)
          getRouteIds(currentRole.id).then(response= > {
            if (response.status === 200 && response.data.status === 200) {
              const routeIds = response.data['data']
              window.sessionStorage.setItem('routeData'.JSON.stringify(routeIds))
            } else {
              Message.error('response.status=' + response.status + 'response.text=' + response.text)
            }
          })
          resolve(useBaseInfo)
        } else {
          Message.error('user login failed')
          resolve()
        }
      }).catch(error= > {
        console.error(error)
        reject(error)
      })
    })
  },

  // Get user information, deprecated
  /** 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, name, avatar, introduction } = 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', name) commit('SET_AVATAR', avatar) commit('SET_INTRODUCTION', introduction) resolve(data) }).catch(error => { reject(error) }) }) }, */

  // The user logs out
  logout({ commit, state, dispatch }) {
    return new Promise((resolve, reject) = > {
      logout(state.token).then(() = > {
        commit('SET_TOKEN'.' ')
        commit('SET_ROLES', [])
        commit('SET_NAME'.' ')
        commit('SET_CURRENT_ROLE'.null)
        window.sessionStorage.removeItem('userInfo')
        window.sessionStorage.removeItem('routeIds')
        window.sessionStorage.removeItem('roles')
        window.sessionStorage.removeItem('currentRole')
        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()
    })
  },

  // Switch roles
  changeRoles({ dispatch }, roleId) {
    return new Promise(async resolve => {
      resetRouter()
      // generate accessible routes map based on roles
      getRoutes(roleId).then(response= > {
        if (response.status === 200 && response.data.status === 200) {
          const dynamicRouteData = response.data['data']
          window.sessionStorage.setItem('routeData'.JSON.stringify(dynamicRouteData))
          // dynamically add accessible routes
          dispatch('permission/generateRoutes', dynamicRouteData)
          // reset visited views and cached views
          dispatch('tagsView/delAllViews'.null)}else {
          Message.error('response.status=' + response.status + 'response.text=' + response.text)
        }
      })
      resolve()
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}
Copy the code

Vuex stores global shared data together with sessionStorage

2.4 to modifysrc/store/modules/permission.jsfile

import { constantRoutes } from '@/router'
// import { getRoutes } from '@/api/role'
import { Message } from 'element-ui'
import { allRouteComponentMap, asyncRoutes } from '@/router/index'
/**
 * Use meta.role to determine if the current user has permission
 * @param roles
 * @param route* /
/** function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)) } else { return true } }*/

/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param roles* /
/** export function filterAsyncRoutes(routes, roles) { const res = [] routes.forEach(route => { const tmp = { ... route } if (hasPermission(roles, tmp)) { if (tmp.children) { tmp.children = filterAsyncRoutes(tmp.children, roles) } res.push(tmp) } }) return res }*/

/** * This is a function that converts routing data into routing components in the previous scheme@param routes
 * @returns {Array}* /
export function transferDynamicRoutes(routes) {
  const routeVos = []
  if(! routes || routes.length ===0) return routeVos
  const length = routes.length
  for (let i = 0; i < length; i++) {
    const item = routes[i]
    let routeComponent
    if (item.componentUrl) {
      if (allRouteComponentMap[item.componentUrl]) {
        routeComponent = allRouteComponentMap[item.componentUrl]
      } else {
        routeComponent = allRouteComponentMap['@/views/error-page/404']}}else {
      routeComponent = null
    }
    const routeVo = { id: item.id, path: item.path, redirect: item.redirect ? item.redirect : 'noRedirect'.name: item.name, alwaysShow: item.name === 'Permission'.hidden: item.hidden,
      meta: { title: item.title,
        icon: item.icon,
        noCache: true
      },
      children: [].component: routeComponent
    }
    if (item.children.length > 0) {
      routeVo.children = transferDynamicRoutes(item.children)
    }
    routeVos.push(routeVo)
  }
  return routeVos
}

const state = {
  routes: [].addRoutes: []}const mutations = {
  SET_ROUTES: (state, routes) = > {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({ commit }, routeIds) {
    const routeIdMap = {}
    for (let i = 0; i < routeIds.length; i++) {
      routeIdMap[routeIds[i]] = routeIds[i]
    }
    return new Promise((resolve) = > {
      if (routeIds && routeIds.length > 0) {
        const dynamicRoutes = filterPermissionRoutes(routeIdMap, asyncRoutes)
        commit('SET_ROUTES', dynamicRoutes)
        resolve(dynamicRoutes)
      } else {
        // throw new Error('transferDynamicRoutes error')
        Message.error('transferDynamicRoutes error')
        resolve([])
      }
    })
  }
}

/** * Return the assembled routeIdMap to obtain the filtered dynamic route set *@param {Object} routeIdMap
 * @param {Array} dynamicRoutes
 * @returns permissionRoutes* /
export function filterPermissionRoutes(routeIdMap, dynamicRoutes) {
  const permissionRoutes = []
  for (let i = 0; i < dynamicRoutes.length; i++) {
    const routeItem = dynamicRoutes[i]
    if (routeIdMap[routeItem.id]) {
      const permissionRouteItem = {
        id: routeItem.id,
        path: routeItem.path,
        name: routeItem.name,
        alwaysShow: routeItem.alwaysShow ! =null && routeItem.alwaysShow,
        redirect: routeItem.redirect,
        meta: routeItem.meta,
        hidden: routeItem.hidden ! =null && routeItem.hidden,
        component: routeItem.component,
        children: []
      }
      permissionRoutes.push(permissionRouteItem)
      if (routeItem.children && routeItem.children.length > 0) {
        permissionRouteItem.children = filterPermissionRoutes(routeIdMap, routeItem.children)
      }
    }
  }
  return permissionRoutes
}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

Copy the code

2.5 to modifysrc/store/getter.jsfile

const getters = {
  sidebar: state= > state.app.sidebar,
  size: state= > state.app.size,
  device: state= > state.app.device,
  visitedViews: state= > state.tagsView.visitedViews,
  cachedViews: state= > state.tagsView.cachedViews,
  token: state= > state.user.token,
  avatar: state= > state.user.avatar,
  name: state= > state.user.name,
  // introduction: state => state.user.introduction,
  roles: state= > state.user.roles,
  permission_routes: state= > state.permission.routes,
  dynamicRoutes: state= > state.permission.addRoutes,
  errorLogs: state= > state.errorLog.logs
}
export default getters
Copy the code

2.6 to modifysrc/permission.jsfile

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

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

const whiteList = ['/login'.'/auth-redirect'] // 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
  const permissionRoutes = store.getters.permission_routes
  const dynamicRoutes = store.getters.dynamicRoutes
  const roleId = getToken()
  if(! permissionRoutes || permissionRoutes.length ===0) {
    // If the fixed route has not been added to the routing object, add the fixed route list first
    router.addRoutes(constantRoutes)
  }
  if (dynamicRoutes && dynamicRoutes.length > 0) {
    // The user has logged in. If you want to go to the login page, go to the home page
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    } else {
      next()
    }
  } else {
    /* The dynamic routing list has not been added to the routing object */
    if(whiteList.indexOf(to.path) ! = = -1) {
      // Whitelist routes enter directly
      next()
    } else {
      // The user has logged in
      if (roleId) {
        // Check whether sessionStorage has saved routing data
        const routeIdsJson = window.sessionStorage.getItem('routeIds')
        if (routeIdsJson) {
          const routeIds = JSON.parse(routeIdsJson)
          store.dispatch('permission/generateRoutes', routeIds).then(response= > {
            if (response && response.length > 0) {
              const dynamicRoutes = response
              router.addRoutes(dynamicRoutes)
              next()
            } else {
              // Get the role's dynamic routing array is empty
              window.sessionStorage.removeItem('routeData')
              Message.warning('the permission routes belong to the current role is empty')
              next()
            }
          })
        } else {
          getRouteIds(roleId).then(response= > {
            if (response.status === 200 && response.data.data.length > 0) {
              const routeIds = response.data.data
              window.sessionStorage.setItem('routeIds'.JSON.stringify(routeIds))
              store.dispatch('permission/generateRoutes', routeIds).then(response= > {
                if (response && response.length > 0) {
                  const dynamicRoutes = response
                  router.addRoutes(dynamicRoutes)
                  next()
                } else {
                  // Get the role's dynamic routing array is empty
                  window.sessionStorage.removeItem('routeIds')
                  Message.warning('the permission routes belong to the current role is empty')
                  next()
                }
              })
            } else {
              // Failed to obtain the role dynamic route
              window.sessionStorage.removeItem('routeIds')
              Message.warning('failed to get permission routes belong to the current role ')
              next()
            }
          }).catch(error= > {
            // Failed to invoke the interface for obtaining the dynamic routing list. You need to log in again
            Message.error(error)
            removeToken()
            if (window.sessionStorage.getItem('userInfo')) {
              window.sessionStorage.removeItem('userInfo')
            // eslint-disable-next-line no-trailing-spaces
            }    
            next(`/login? redirect=${to.path}`)
            NProgress.done()
          })
        }
      } else {
      // The user does not log in and is redirected to the login page
        removeToken()
        next(`/login? redirect=${to.path}`)
        NProgress.done()
      }
    }
  }
})

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

The logic of dynamic loading routing menu is implemented in router.beforeEach guard function. The modification in this file is the key to achieve dynamic rendering menu.

2.7 to modifysrc/views/index.vuefile

Change the user name and password in the login component to the values when you invoke the registration interface through Postman

 data() {
    const validateUsername = (rule, value, callback) = > {
      if(! validUsername(value)) { callback(new Error('Please enter the correct user name'))}else {
        callback()
      }
    }
    const validatePassword = (rule, value, callback) = > {
      if (value.length < 6) {
        callback(new Error('The password can not be less than 6 digits'))}else {
        callback()
      }
    }
    return {
     // The values of username and password in loginForm are modified
     // You can delete the original user name and password in the input box and enter the correct user name and password
     loginForm: {
        username: 'heshengfu'.password: 'heshengfu123'
      },
      loginRules: {
        username: [{ required: true.trigger: 'blur'.validator: validateUsername }],
        password: [{ required: true.trigger: 'blur'.validator: validatePassword }]
      },
      passwordType: 'password'.capsTooltip: false.loading: false.showDialog: false.redirect: undefined.otherQuery: {}}}Copy the code

2.8 to modifysrc/utils/validate.jsfile

Modify the validUsername method, the original project can only be admin and editor two users

export function validUsername(username) {
  if (username == null || username.trim() === ' ') {
    Message.error('User name cannot be empty')
    return false
  }
  return true
}
Copy the code
2.9 to modifybuild/index.jsandvue.config.jsfile

(1) Change the port number in build/index.js to 3000. Readers can also change the port number to any secure port that is not occupied

if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
  const report = rawArgv.includes('--report')

  run(`vue-cli-service build ${args}`)
  // Set the port to 3000
  const port = 3000
  const publicPath = config.publicPath

  var connect = require('connect')
  var serveStatic = require('serve-static')
  const app = connect()
  app.use(
    publicPath,
    serveStatic('./dist', {
      index: ['index.html'.'/']}))Copy the code

(2) Comment out proxy forwarding in the vue.config.js file

// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {
  /** * You will need to set publicPath if you plan to deploy your site under a sub path, * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, * then publicPath should be set to "/bar/". * In most cases please use '/' !!! * Detail: https://cli.vuejs.org/config/#publicpath */
  publicPath: '/'.outputDir: 'dist'.assetsDir: 'static'.lintOnSave: process.env.NODE_ENV === 'development'.productionSourceMap: false.devServer: {
    port: port,
    open: true.overlay: {
      warnings: false.errors: true
    }
    // proxy: {
    // '/api': {
    // target: 'http://localhost:8081/blog',
    // changeOrigin: true,
    // pathRewrite: {
    // '^/api': 'http://localhost:8081/blog'
    / /}
    / /}
    // }
    // after: require('./mock/mock-server.js')
  },
Copy the code

Since our backend service is set up across domains, proxy forwarding is not required

2.10 to modify.env.developmentfile

# base api
#VUE_APP_BASE_API = '/api'
VUE_APP_BASE_API = 'http://localhost:8081/blog'
port = 3000
Copy the code

Change the VUE_APP_BASE_API to backend API request prefix, i.e. http://localhost:8081/blog

That’s all you need to change at the front desk

3 Effect Experience

3.1 Starting front and Background Services

Start the background service first. Before starting the background service, start the local mysql service to prevent errors due to the mysql database connection failure

After modifying the js files in the vue-element-admin project, right-click the vue-element-admin project root directory ->git bash and type NPM run dev in the console that pops up

If the following information is displayed on the console, the front-end service is successfully started:

App running at:
  - Local:   http://localhost:3000/
  - Network: http://192.168.1.235:3000/
Copy the code

3.2 Login and Go to the Home page

After the front-end service is successfully started, the browser will automatically open and jump to the login page, as shown in Figure 1

Figure 1 The login interface is displayed after the front-end project is successfully started

Click the Login button to jump to the vue-element-Admin project home page

Figure 2 Enter the project home page after successful login

During the login process, we can enter developer mode by clicking the right mouse button -> Check to view the network request initiated by the browser. We can clearly see the interface of user login success and the interface of querying the routing resource list according to the role ID

Figure 3. Login request header

Figure 4 Login request response data preview

Figure 5 Obtaining the precheck request for the current role routing ID set data

Figure 6 GET request for the current role routing ID collection data

Figure 7 Successful response of the interface to get the current role routing ID set data

After entering the home Page, we clicked the sub-menu Page Permission under the dynamically loaded route Permission menu and found that we could enter the Permission control Page smoothly without the problem of 404 reported when the whole route component was dynamically loaded from the background.

Figure 8 Enter the dynamic control menu word menu Page Permission Page

At this point, the most difficult part of using VUE and VUe-Router with spring-boot technology to dynamically load menus based on roles and access pages by permission is almost here! In the future, THE author will make persistent efforts to achieve a complete set of permission control system such as assigning roles to users, allocating resources to roles and implementing button granularity permission control with Spring-Security on this basis. Please look forward to it!

Vue-element-admin project is a very famous open source project in China. At present, there are more than 40,000 start projects on Github. The author of the project is PanJiaChen, who works for toutiao, a well-known Internet company in China. The author of the rights control column address: hand touch, take you with vue wanbackstage series two (login rights) (juejin. Cn), interested readers can have a good look.

The implementation of this article relies on an in-depth study of the vue-element-admin project source code, In particular, permission.js in SRC, permission.js and user.js in SRC /store/module, and index.vue in menu-related SRC /layout

And the components/Sidebar/SidebarItem vue and conponents/AppMain vue source of several important documents such as in-depth study. Readers who need to make changes to the Vue-element-admin project are advised to focus on the source code in these files.

5 Recommended Reading

[1] Implementation of User Role based page routing resource permission control (Backend)

[2] VBlog, an open source blog project, is introduced and packaged and deployed under Nginx servers that already run multiple Web projects

[3] SpringBoot project integrates Ali Cloud object storage service to realize file uploading

[4] Learn how to deploy front-end Web projects using Nginx servers

[5] Luban-Mall, a micro e-commerce project with rich technology stack, is strongly recommended

All the modified codes in the front end of this dimension have been uploaded to the author’s personal Gitee warehouse. Interested friends follow the author’s wechat public account [Fu On Java Technology Stack] and send keywords [VUe-element-admin]. Readers who have been concerned can directly send this message to get the address of the front-end code warehouse after finding “A Fu talking about Java Technology Stack” in the wechat public account.

This article is long, belongs to the ten thousand word long article, may be very difficult for readers to have the patience to finish reading, feel helpful to your work of readers suggested to collect first. This article first personal wechat public number [Fu talk about Java technology stack], welcome readers to pay attention to the author’s wechat public number, more dry goods article let us grow together on the road of technology!

—END—