preface

First of all, thank you for your great praise, your support is my motivation! I wrote one last weekBuild the Element background frame from 0 to 1, a lot of children shoes comments mentioned permission issues, this week I will give you up.GitHub

1. JWT Authorization

At present, most projects adopt JWT authorization authentication, which is also the token login identity verification mechanism we are familiar with. JWT has many benefits. Because JWT is generated by the server, the server will verify the security and effectiveness after the middleman changes the key string. It usually stays in the Authorization header of the request. The front-end children usually obtain tokens and store them through VUEX, and then the data is stored in the session persistently.

Route redirect verifies the token

First, verify whether vuEX stores a token during route redirection. If no token is stored, jump to the login page to obtain the token.

    if(to.path ! = ='/login' && !store.state.token) {
        next('/login')
        NProgress.done() / / end of Progress
      } else {
        next();
     }
Copy the code

Request interception carries a token

After the token exists locally in router.js, we need to bring the token each time we request the interface to verify the validity of the token.

    // Intercept before request
    if (store.state.token) {
            config.headers["Authorization"] = "Bearer " + store.state.token;
        }
Copy the code

If the token is not valid, the global error is handled and the login page is directly jumped

     case 401:
        messages("warning"."User login expired, please log in again.");
        store.commit('COMMIT_TOKEN'.' ')
        setTimeout((a)= > {
            router.replace({
                path: "/login".query: {
                    redirect: router.currentRoute.fullPath
                }
            });
        }, 1000);
        break;
Copy the code

See request.js in the project for more code

2. Menu permissions

In this project, I mainly judge the display and hiding of navigation menu by the role type passed from the back end. Roles =[‘admin’] and roles=[‘user’]. Next, I designed a menu table and a routing table. The routing table is mainly used to register routes without considering hierarchy. The menu table needs to consider the hierarchy, which can configure the main menu, sub-menu, icon and a series of things, of course, the menu table is best through the interface data from the back end. Note that there is a meta configuration item in both the menu table and the routing table. It can configure our role permissions. The role permissions of the menu table corresponding to the routing table must be consistent. Menus that do not have role permissions are open by default.

menu.js

    {
        icon: "el-icon-question".index: "premission".title: "Permission Tests".subs: [{
            index: "permission".title: "Menu test".meta: {
                roles: ['admin']}}, {index: "permissionBtn".title: "Button Permissions",]}}Copy the code

router.js

   {
      path: '/permission'.component: getComponent('permission'.'permission'),
      meta: {
        title: 'Menu Permissions'.roles: ['admin']}},Copy the code

Filter menus by role

Now we start writing the menu logic, go Aside. Vue, first filter the menu table menu.js by role

    /** * @param {Arrary} menus * @param {Arrary} roles * @return {Arrary} res filtered menu */
    filterMenus(menus, roles) {
      const res = [];
      menus.forEach(route= > {
        consttmp = { ... route };//hasPermission Checks whether permissions match
        if (this.hasPermission(roles, tmp)) {
          if (tmp.subs) {
            tmp.subs = this.filterMenus(tmp.subs, roles); } res.push(tmp); }});return res;
    },
Copy the code
    /** * Check whether meta. Role matches the current user's permission * @roles * @param menu */
    hasPermission(roles, menu) {
      if (menu.meta && menu.meta.roles) {
        return roles.some(role= > menu.meta.roles.includes(role));
      } else {
        return true; }},Copy the code

Filter results

    computed: {
        items() {
          let items = this.filterMenus(menu, this.$store.state.roles);
          returnitems; }},Copy the code

So far, the permission control is basically complete, but in the process of running the project, there is also a bug. In this project, there is a tagList, which is the open navigation TAB. When the user switches from admin to user, the navigation TAB remains, which means that the user can access the Premission page through the navigation TAB. At this point, I directly through route interception to deal with this situation.

    if(to.meta.roles){ to.meta.roles.includes(... store.getters.roles)? next():next('/ 404')}else{
        next();
     }
Copy the code

All pages without permission will go to 404 page.

3. Button permission control

Button level permissions, to be honest, are generally controlled through the data interface to show, click, and so on. If light has a front end to control it is definitely not a viable option. Project button permissions to register global custom directives to do. First, create a directive folder under SRC to register global directives. Create a new premissionbtn.js folder. Consult the official documentation if you are not familiar with custom directives.

Global directives

    import Vue from 'vue'
    import store from '@/store/store'
    // Register a V-allowed directive
     Vue.directive('allowed', {
        inserted: function (el, bingding) {
            let roles = store.getters.roles
            // Determine permissions
            if (Array.isArray(roles) && roles.length > 0) {
                let allow = bingding.value.some(item= > {
                    return roles.includes(item)
                })
                if(! allow) {if (el.parentNode) {
                        el.parentNode.removeChild(el)
                    }
                }
            }
        }
    })
Copy the code

reference

    import './directive/premissionBtn'
Copy the code

So how do custom directives work?

     <div class="premissionBtn">
        <el-button type="primary" v-allowed="['admin']">I am only admin when the display</el-button>
        <br>
        <el-button type="info" v-allowed="['user']">I can only show it when I'm a user</el-button>
        <br>
        <el-button type="warning" v-allowed="['admin','user']">Only if I am admin or user</el-button>
        <br>
        <el-button type="danger">Any role can be displayed</el-button>
    </div>
Copy the code

Afterword.

There are still many areas to be improved and optimized in this project. If there are any deficiencies or better methods in the project, please timely propose them for easy correction. Thank you.

Build the Element framework from 0 to 1