Skip to content

Access Control

NPM Version

For frontend applications, "access control" refers to whether pages are accessible and whether page elements are visible.

Setup

  1. Install the plugin
bash
$ npm add @winner-fed/plugin-access -D
bash
$ yarn add @winner-fed/plugin-access -D
bash
$ pnpm add @winner-fed/plugin-access -D
bash
$ bun add @winner-fed/plugin-access -D
  1. Enable the plugin in the .winrc configuration file
ts
import { defineConfig } from 'win';

export default defineConfig({
  plugins: ['@winner-fed/plugin-access'],
  routes: [{
    path: '/',
    name: 'Home',
    component: 'index',
    routes: [
      {
        path: 'admin',
        name: 'Admin',
        component: '@/components/admin'
      },
      {
        path: 'normal',
        name: 'Normal',
        component: '@/components/normal'
      }
    ]
  }],
  /**
   * @name Access Control Plugin
   * @doc https://winjs-dev.github.io/winjs-docs/plugins/access.html
   */
  access: {
    roles: {
      admin: ['/admin'],
      normal: ['/normal']
    }
  }
});

Introduction

In this plugin design, one page permission corresponds to one route. For example, when accessing the '/admin' route, if the current role doesn't have permission for this route, the component will not be rendered.

ts
export default {
  access: {
    roles: {
      admin: ['/admin'],
      normal: ['/normal']
    }
  }
};

Usage example:

vue

<template>
  <Access :id="accessId">Only visible to Admin</Access>
  <div v-access="accessId">Only visible to Admin</div>
</template>

<script setup lang="ts">
const accessId = '/admin';
</script>

Note

We uniformly refer to pages and page elements as resources, and use resource IDs to identify and distinguish them:

  • The resource ID of a page defaults to the page's route path. For example, the route path of page pages/a.vue is /a. When the page accesses /a, the current page will be rendered, and /a is the page's accessId.
  • Page elements' resource IDs have no default value and need to be customized. :::

Matching Rules

Exact Matching

The default matching rule for resources is exact matching. For example, if page pages/a.vue corresponds to route path /a, then /a is the page's resource ID. If we set:

js
access.setAccess(['/a']);

Since the permission list contains /a, it indicates having permission for this page.

Fuzzy Matching

Page @id.vue will be mapped to dynamic route /:id. There are two ways to match this page:

  • access.setAccess(['/:id'])
  • access.setAccess(['/*'])

The second is fuzzy matching, where * represents any path. For example, if role admin needs full permissions:

js
export default {
  access: {
    roles: {
      admin: ['*']
    }
  }
};

Compile-time Configuration

When executing win dev or win build, runtime code is generated through this configuration. Configure in the .winrc.js configuration file:

js
export default {
  access: {
    roles: {
      admin: ['/', '/admin', '/normal'],
    },
  },
};

roles

  • Type: Record<string, []>;

  • Default: {}

  • Details:

    Predefined role list. key is the role ID, and value is the resource list corresponding to the role ID.

Runtime Configuration

Configure in app.js

unAccessHandler

  • Type: Function

  • Default: null

  • Details: When entering a route, if the page corresponding to the route doesn't belong to the visible resource list, entry will be paused and the unAccessHandler function will be called.

  • Parameters

    • router: Router instance created by createRouter
    • to: Route being navigated to
    • from: Route being navigated away from
    • next: next function

Example:

js
import { access as accessApi } from 'winjs';

export const access = {
  unAccessHandler ({ to, next }) {
    const accesssIds = accessApi.getAccess();
    if (to.path === '/404') {
      accessApi.setAccess(accesssIds.concat(['/404']));
      return next('/404');
    }
    if (!accesssIds.includes('/403')) {
      accessApi.setAccess(accesssIds.concat(['/403']));
    }
    next('/403');
  },
};

noFoundHandler

  • Type: Function
  • Default: null
  • Details: When entering a route, if the page corresponding to the route doesn't exist, the noFoundHandler function will be called.
  • Parameters
    • router: Router instance created by createRouter
    • to: Route being navigated to
    • from: Route being navigated away from
    • next: next function

Example:

js
import { access as accessApi } from 'winjs';

export const access = {
  noFoundHandler ({ next }) {
    const accesssIds = accessApi.getAccess();
    if (!accesssIds.includes('/404')) {
      accessApi.setAccess(accesssIds.concat(['/404']));
    }
    next('/404');
  },
};

ignoreAccess

  • Type: Array<string>
  • Default: null
  • Details: Configure pages that should ignore access control validation.

Example:

js
export const access = {
  ignoreAccess: ['/login'],
};

API

WinJS uses custom directives, specifically the v-access directive, passing in the object returned by useAccess to control component rendering.

Plugin APIs are exported through winjs:

js
import { access } from 'winjs';

access.hasAccess

  • Type: ( accessId: string | number ) => Promise\<boolean\>
  • Details: Determine whether a resource is visible.
  • Parameters:
    • accessId: Resource ID
  • Returns: Whether access is granted

access.isDataReady

  • Type: () => boolean
  • Details: Permissions can be set using asynchronous data. isDataReady is used to determine whether asynchronous data has finished loading.
  • Parameters: null
  • Returns: Boolean
js
import { access } from 'winjs';

console.log(access.isDataReady());

access.setRole

  • Type: Function
  • Details: Set the current role.
  • Parameters:
  • roleId: Role ID, has two types:
    • String: Corresponds to the key in the roles configuration object.
    • Promise: The result of Promise resolve should correspond to the key in the roles configuration object.
js
import { access } from 'winjs';

access.setRole('admin');

access.getRole

  • Type: Function
  • Details: Get the current role.
js
import { access } from 'winjs';

access.getRole();

access.setAccess

  • Type: Function
  • Details: Set the current access permissions.
  • Parameters:
  • accessIds: Array of resource IDs, has two types:
    • Array: Array items correspond to the key in the roles configuration object.
    • Promise: The result of Promise resolve should be Array<accessId>.
js
import { access } from 'winjs';

access.setAccess(['/a', '/b', '/c']);

access.getAccess

  • Type: Function
  • Details: Return the current list of visible resources.
  • Parameters: null
js
import { access } from 'winjs';

access.getAccess();

useAccess

  • Type: Composition function
  • Details: Determine whether a resource is visible.
  • Parameters:
    • accessId: Resource ID
  • Returns: ref
vue

<template>
  <div v-if="accessOnepicess">accessOnepicess</div>
</template>
<script>
import { useAccess } from 'winjs';

export default {
  setup () {
    const accessOnepicess = useAccess('/onepiece1');
    return {
      accessOnepicess,
    };
  },
};
</script>

v-access

Pass accessId into the v-access directive. When accessId has permission, the DOM will be displayed; when there's no permission, the DOM will be hidden.

vue

<template>
  <div v-access="accessId">accessOnepicess</div>
</template>
<script>
export default {
  setup () {
    return {
      accessId: 'accessOnepicess',
    };
  },
};
</script>

Access Component

Pass accessId into the Access component. When accessId has permission, this component will be rendered; when there's no permission, this component will be hidden.

vue

<template>
  <access :id="accessId"> accessOnepicess</access>
</template>
<script>
export default {
  setup () {
    return {
      accessId: 'accessOnepicess',
    };
  },
};
</script>

Released under the MIT License.