vue from zero to build a front middle and back office Rights Management Templates

background

Our products need to have a lot of rights management. There is a product, before the need to deploy multiple clients in the background. In the development of the first version, code-all. Three front-end, back-end three sets. Plus kafka, redis, algorithms, database server, every new customer would have a need to deploy once, it takes a long time and the code difficult to maintain.

After deciding to refactor the code, before the products are divided into, during and after the three platforms. Front and rear ends, respectively, of a code, support for rights management, can be expanded. Front-end platform using the route prefix judge, will return different token and user login information. Different token can only access the interface corresponds to the platform, the Build menu accessible based on user roles, go to a different system

Foreword

Permissions module is more troublesome part for a project, usually a project of rights management, need to do is the following three levels of authentication.

  1. Platform level
  2. Page level (menu)
  3. Level controls (such as buttons, tables showing fields, etc.)

This article standing in front of the point of view, to achieve the first two levels of rights management (control levels can be achieved by rendering conditions). Vue in the background with the right management templates from the front to build a zero. For reference.

Demo Address: http://auth.percywang.top

Project Address: https://github.com/pppercyWang/vue-authentication

In fact, most of the projects will separate front and back, because integrated set of code, indeed package optimization, code division needs to do more. And infrastructure projects will be some complex, more comprehensive safety considerations. There is also provided a background of pure rights management templates.

Project Address: https://github.com/pppercyWang/vue-authentication2

Project structure

Technology stack: vue vue-router vuex element

assets  静态资源
plugins
	element-style.scss  element样式
    element.js   按需引入
router
	index.js 静态路由及createRouter方法
service
    api.js  前中后台接口管理
store  vuex
utils
	http.js axios封装
views
	foreground  前台页面
    midground   中台页面
	background  后台页面
    layout    前中后台布局文件
    404.vue   404页面
    Login.vue   前台登录
    AgentLogin.vue   中台登录
    AdminLogin.vue   后台登录
permission.js   动态路由 前中后台鉴权 菜单数据生成
main.js  应用入口

A routing initialization --staticRoutes

Three platforms to log three different pages. / The beginning of the route the front desk, / agent is in Taiwan, / admin is the background. Redirects here can also jump to a specific page, but here because of the different roles of authority reasons, can not be written to death, redirected directly to the login page.

Note: 404 final surface needs to be placed routing, dynamic routing so on the part

router/index.js

const staticRoutes = [{
    path: '/login',
    name: '用户登录',
    component: () => import('@/views/Login.vue'),
  },
  {
    path: '/agent/login',
    name: '中台登录',
    component: () => import('@/views/AgentLogin.vue'),
  },
  {
    path: '/admin/login',
    name: '后台登录',
    component: () => import('@/views/AdminLogin.vue'),
  },
  {
    path: '/',
    redirect: '/login',
  },
  {
    path: '/agent',
    redirect: '/agent/login',
  },
  {
    path: '/admin',
    redirect: '/admin/login',
  },
]

II. Dynamic routing --dynamicRoutes

Only in this case middle and back office for authentication, a column required field icon, the icon for the menu item. children as a sub-section columns, roles array represents the meta accessible role of the route.

permission.js

const dynamicRoutes = {
    // 前台路由
    'user': [{
        path: '/',
        component: () => import('@/views/layout/Layout.vue'),
        name: '首页',
        redirect: '/home',
        children: [{
            path: 'home',
            component: () => import('@/views/foreground/Home.vue'),
        }]
    }, ],
    // 中台路由
    'agent': [{
            path: '/agent/member',
            component: () => import('@/views/layout/AgentLayout.vue'),
            name: '会员管理',
            redirect: '/agent/member/index',
            icon: 'el-icon-star-on',
            children: [{
                    path: 'index',
                    component: () => import('@/views/midground/member/Index.vue'),
                    name: '会员列表',
                    meta: {
                        roles: ['super_agent', 'second_agent'] // 超级代理和二级都可访问
                    },
                },
                {
                    path: 'scheme',
                    component: () => import('@/views/midground/member/Scheme.vue'),
                    name: '优惠方案',
                    meta: {
                        roles: ['super_agent']  // 只有超级代理可访问
                    },
                },
            ]
        },
    ],
    // 后台路由
    'admin': [{
            path: '/admin/user',
            component: () => import('@/views/layout/AdminLayout.vue'),
            name: '用户管理',
            redirect: '/admin/user/index',
            icon: 'el-icon-user-solid',
            children: [{
                    path: 'index',
                    component: () => import('@/views/background/user/Index.vue'),
                    name: '用户列表',
                    meta: {
                        roles: ['super_admin', 'admin']
                    },
                },
                {
                    path: 'detail',
                    component: () => import('@/views/background/user/UserDetail.vue'),
                    name: '用户详情',
                    meta: {
                        roles: ['super_admin']
                    },
                },
            ]
        },
    ],
    '404': {
        path: "*",
        component: () => import('@/views/404.vue'),
    }
}

III. Login page

Usually after a successful login, the backend will return token with user information, we need to token user information with persistence, easy to use, and here I direct presence of sessionStorage. Re-entering the different routes according to different user roles

views/adminLogin.vue

try {
    const res = await this.$http.post(`${this.$api.ADMIN.login}`, this.form.loginModel)
    sessionStorage.setItem("adminToken", res.Data.Token);
    const user = res.Data.User
    sessionStorage.setItem(
        "user",
        JSON.stringify({
            username: user.username,
            role: user.role,
            ground: user.ground // 前中后台的标识  如 fore mid back
        })
    );
    switch (user.role) {
        case "ip_admin": // ip管理员
            this.$router.push("/admin/ip/index");
            break;
        case "admin": // 普通管理员
            this.$router.push("/admin/user/index");
            break;
        case "super_admin": // 超级管理员
            this.$router.push("/admin/user/index");
            break;
    }
} catch (e) {
    this.$message.error(e.Message)
}

IV. Routing guard --router.beforeEach ()

As long as enter the login page, we need to do two things.

  1. token information and user information stored in the clear in the sessionStorage
  2. Use createRouter permission.js provided () creates a new instance of the router, replace matcher.

We are here to use addRoutes Add a new route on the basis of static routes, but the document does not provide delete routes api. Can imagine, if the login and then log back in Taiwan, the situation in Taiwan can access the back-end routing occurs. Why replace matcher can delete a route addRoutes added?

Note: Before router.beforeEach must be placed vue instance is created, or when a page refreshes when the route will not hook into beforeEach

main.js

router.beforeEach((to, from, next) => {
  if (to.path === '/login' || to.path === '/agent/login' || to.path === '/admin/login') {
    sessionStorage.clear();
    router.matcher = createRouter().matcher // 初始化routes,移除所有dynamicRoutes
    next()
    return
  }
  authentication(to, from, next, store, router); //路由鉴权
})
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

V. backstage before the authentication --authentication ()

Here switch function based to.path.split ( "/") [1 ] determined internet. After successful login we sessionStorage.setItem () to save token.
Why use token agentToken adminToken three different key to store it? Rather than just the token as a key yet. Such set axios.interceptors.request.use interceptor token head does not need to get a different token through the switch.

Because we assume that the current page is routing agent / member / index, we manually modify the admin / xxx / xxx. We want it to jump to the admin login page, rather than 404 pages.

isAuthentication identity authentication is completed, there is no authentication is called generateRoutes obtain a valid route, add a new route through addRoutes

permission.js

export function authentication(to, from, next, store, router) {
    let token;
    switch (to.path.split("/")[1]) {
        case 'agent':
            token = sessionStorage.getItem('agentToken');
            if (!token && to.path !== '/agent/login') {
                next({
                    path: '/agent/login'
                })
                return
            }
            break;
        case 'admin':
            token = sessionStorage.getItem('adminToken');
            if (!token && to.path !== '/admin/login') {
                next({
                    path: '/admin/login'
                })
                return
            }
            break;
        default:
            token = sessionStorage.getItem('token');
            if (!token && to.path !== '/login') {
                next({
                    path: '/login'
                })
                return
            }
            break;
    }
    const isAuth = sessionStorage.getItem('isAuthentication')
    if (!isAuth || isAuth === '0') {
        store.dispatch('getValidRoutes', JSON.parse(sessionStorage.getItem('user')).role).then(validRoutes => {
            router.addRoutes(validRoutes)
            sessionStorage.setItem('isAuthentication', '1')
        })
    }
    next();
}

Judging by user.ground platform

store/index.js

   getValidRoutes({commit}, role) {
      return new Promise(resolve => {
        let validRoutes
        switch (JSON.parse(sessionStorage.getItem('user')).ground) {
          case 'fore':
            validRoutes = generateRoutes('user', role, commit)
            resolve(validRoutes);
            break
          case 'mid':
            validRoutes = generateRoutes('agent', role, commit)
            resolve(validRoutes);
            break
          case 'back':
            validRoutes = generateRoutes('admin', role, commit)
            resolve(validRoutes);
            break
        }
      })
    },

VI. Role screening --generateRoutes ()

Here did two of the most important things

  1. Generating menu data in the el-menu
  2. Generate effective role of the current route

permission.js

export function generateRoutes(target, role, commit) {
    let targetRoutes = _.cloneDeep(dynamicRoutes[target]);
    targetRoutes.forEach(route => {
        if (route.children && route.children.length !== 0) {
            route.children = route.children.filter(each => {
                if (!each.meta || !each.meta.roles) {
                    return true
                }
                return each.meta.roles.includes(role) === true
            })
        }
    });
    switch (target) {
        case 'admin':
            commit('SET_BACKGROUD_MENU_DATA', targetRoutes.filter(route => route.children && route.children.length !== 0)) // 菜单数据是不需要404的
            break
        case 'agent':
            commit('SET_MIDGROUD_MENU_DATA', targetRoutes.filter(route => route.children && route.children.length !== 0))
            break
    }
    return new Array(...targetRoutes, dynamicRoutes['404'])
}

VII. After the page refreshes data loss

After logging isAuthentication 1, will not regenerate the routing is refreshed, resulting in data loss, in main.js listening to window.onbeforeunload

main.js

window.onbeforeunload = function () {
  if (sessionStorage.getItem('user')) {
    sessionStorage.setItem('isAuthentication', '0') // 在某个系统登录后,页面刷新,需重新生成路由
  }
}
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

expand

It is almost time you're done, simply render data to el-menu can be.

1. Background control authority

Current routing authentication control substantially by the front end, a rear end platform identification just return and role. However, the actual development, through the background are sure to storage control, menus and other information needed to build character table. To modify the column name, a column icon, menu and other rights
we can get a table at getValidRoutes permission, it will insert the data into dynamicRoutes in. The rear end of the returned data as follows:

[{
        id: 1,
        name: '用户管理',
        icon: 'el-icon-user-solid',
        children: [{
                id: 3,
                name: '用户列表',
                meta: {
                    roles: [1, 2]
                },
            },
            {
                id: 4,
                path: 'detail',
                name: '用户详情',
                meta: {
                    roles: [1]
                },
            },
        ]
    },
    {
        id: 2,
        name: 'IP管理',
        icon: 'el-icon-s-promotion',
        children: [{
            id: 5,
            name: 'IP列表',
            meta: {
                roles: [1, 2, 3]
            },
        }, ]
    },
]

2. safety

front end:

  1. Cross-platform into the routing platform to jump directly to the login page.
  2. The current platform does not have permission to access a page report 404 errors.

rear end:

  1. It must ensure that the appropriate platform token can only transfer the corresponding interface, otherwise an error.
  2. If we could do the role of the interface authentication better, rejected a request from the interface level

3.axios package

Request interceptor take different token user information, the header information provided
in the response interceptor, if the token expires, then jump to the login page and different information

4.api management

If the backend is also a code. Api that can be managed this way, but if you do not have a unified prefix. Can for example proxy, this would resolve the problem of cross-domain in axios set up a unified prefix.

const USER = 'api'
const AGENT = 'agent'
const ADMIN = 'admin'
export default {
  USER: {
    login: `${USER}/User/login`,
  },
  AGENT: {
    login: `${AGENT}/User/login`,
    uploadFile: `${AGENT}/Utils/uploadFile`,
  },
  ADMIN: {
    login: `${ADMIN}/User/login`,
  },
}
devServer: {
    proxy: {
      '/proxy': {
        target: 'http://localhost:8848',
        changeOrigin: true,
        pathRewrite: {
          '^proxy': ''  //将url中的proxy子串去掉
        }
      }
    }
  },
Published 51 original articles · won praise 18 · views 40000 +

Guess you like

Origin blog.csdn.net/weixin_42565137/article/details/103944409