An authentication solution based on Vue.js v2.x, including elements-based control and route-based control.
中文指南 | English
Dependencies | Required |
---|---|
vue | ✔️ |
vue-router | ✔️ |
-
Minimal design: Only one ability/privilege list and give you all element-based and route-based authentications.
-
Smooth changes: Support any dynamic private routes addition and deletion without page reloading.
# using npm
npm i v-access
# using yarn
yarn add v-access
-
Ability type
type Ability = string
Ability
should be a global unique identifier and string type. -
Routes type
interface RouteWithAbility extends RouteConfig { readonly children?: RouteWithAbility[] readonly meta?: { strict?: Ability[] weak?: Ability[] ability?: Ability [key: string]: any } }
More details could be found from here.
NOTICE: This section is only the best practice recommendation, not required.
The entire authorization system is based on an ability/privilege list provided by any back-end services. Every element in the list represents an ability that is used to access the corresponding database. A user role consists of multiple abilities, represents an ability set. One actual user could have multiple user roles.
There is a best practice that uses syntax like [scope].[module].[ability]
(eg. IAM) to represents one ability. In this case, [scope]
is optional if you have no other external systems (scope).
The advantage of this design is that the role of multiple users or the ability of multiple roles can be intersected. Multiple abilities can be arbitrarily combined to form a flexible abilities set.
The following chart represents an actual user's ability set:
+--> github.repo.read
+-> user role 1 |
| +--> npm.org.import
|
| +--> github.pull.read
user -+-> user role 2 |
| +--> npm.downloads.read
|
| +--> github.action.read
+-> user role 3 |
+--> npm.packages.publish
No matter what your ability name is, you should always call init function with a full ability list first.
import Vue from 'vue'
import VAccess from 'v-access'
Vue.use(VAccess)
This package should be installed before the root Vue instance creation. This process will inject a global component named VAccess
and a prototype property named $$auth
.
import { init } from 'v-access'
export default {
name: 'AnyComponent',
// ... omit all unrelated properties
created() {
// a vuex action or http request
fetchAbilities(payload)
.then(list => list.map(abilityInfo => ability.name)) // ability serialization
.then(abilities =>
init({
vm: this, // or this.$router
abilities,
redirect: '/forbidden',
routes: [
/* routes which need to add to vue-router would be filtered by abilities first */
]
})
)
.catch(console.error)
}
}
No matter the original abilities structure is, you should always pass an Ability identity
list (a string[]
type) to init
function for initializing global authentication functionality.
interface InitOptions {
vm: Vue | VueRouter
abilities: Ability[]
redirect: string
routes?: RouteWithAbility[]
}
export declare function init({
vm,
abilities,
redirect,
routes
}: InitOptions): void
NOTE: redirect
only support a fullPath string, not object type.
As you may have noticed, you can pass a global preset private routes collection to init
function for dynamic routes addition. All valid private routes generation could be handled by this package and will be filtered by abilities
set.
This case would be useful when you want to create private routes that need to be filtered by the current user abilities.
-
Using
element-based
authenticationThe results of the following two authentication ways are reactive.
-
VAccess
component<v-access :ability="['github.repo.read', 'github.repo.pull']"> <!-- any child components or HTML nodes --> </v-access> <!-- or --> <v-access strict :ability="['github.repo.read', 'github.repo.pull']"> <!-- any child components or HTML nodes --> </v-access> <!-- or --> <v-access :ability="github.repo.read"> <!-- any child components or HTML nodes --> </v-access>
Props Type Description ability Ability
orAbility[]
An ability or ability set that needs to be authenticated strict boolean
Whether we should authenticate every abilities in the list -
$$auth
objectThe following table describes several
$$auth
authentication functions.Function Type Description has (ability: Ability) => boolean
An ability that needs to be authenticated verifyAll (abilities: Ability[]) => boolean
Whether we should authenticate every ability in the list verifySome (abilities: Ability[]) => boolean
Whether we should authenticate at least one ability in the list
-
-
Using
route-based
authenticationconst routes = [ // This route always pass authentication { name: 'PublicRoutes', path: '/public', component: () => import(/* webpackChunkName: 'page-public' */ './views/Public.vue') }, { name: 'PrivateRoutes', path: '/private', component: () => import(/* webpackChunkName: 'page-private' */ './views/Private.vue'), meta: { strict: ['github.repo.read', 'github.repo.pull'] // or // weak: ['github.repo.read', 'github.repo.pull'], // or // ability: 'github.repo.read' } } ]
Meta prop Objective strict
Whether we should authenticate every ability in the list weak
Whether we should authenticate at least one ability in the list ability
A single ability that needs to be authenticated
export declare function reset(router: VueRouter): void
You should always use reset(theCurrentRouterInstance)
to delete all private routes added by init
function without any page reloading.
import { reset } from 'v-access'
reset(this.$router)
Separation of concerns is a design principle for separating distinct parts, and implement the high cohesion and low coupling between multiple independent parts. Vue router navigation guard accepts multiple hooks to implement a navigation pipeline via this principle. This is the theoretical basis for v-access
implementation. v-access
has provided an authorizer
as a beforeEach
guard.
If you aren't familiar with how multiple global beforeEach
hooks work, I strongly recommend you to read the documentation about router.beforeEach
.
All notable changes to this package will be documented in CHANGELOG file.
MIT © Bowen Liu