<>Vue Front end authentication scheme under the separation of front end and back end
<> Technology stack
front end Vue Family bucket , backstage .net.
<> requirement analysis
* Front end routing authentication , Shield address bar intrusion
* Routing data is managed by the background , The front end only loads routes asynchronously according to fixed rules
* Access control is accurate to every button
* Auto update token
* The same browser can only log in to one account
<> Front end solution
For demand 1,2,3, Asynchronous loading routing scheme is adopted
* Write first vue Global routing guard
* Exclude login route and authentication free route
* Request to pull user menu data after login
* stay vuex Processing menu and route matching data in
* Will be in vuex The routing data processed in the addRoutes Asynchronous push routing router.beforeEach((to, from, next) => {
// Judge whether the current user has pulled the permission menu if (store.state.sidebar.userRouter.length === 0) { // Pull without menu
getMenuRouter() .then(res => { let _menu = res.data.Data.ColumnDataList || [];
// if (res.data.Data.ColumnDataList.length > 0) { // Organize the menu & Routing data
store.commit("setMenuRouter", _menu); // Push permission routing list
router.addRoutes(store.state.sidebar.userRouter); next({...to, replace: true
}); // } }) .catch(err => { // console.log(err); // Message.error(" Server connection failed ");
}); } else { // When you have user rights , Indicates that all accessible routes have been generated If you don't have permission to access the menu, it will enter automatically 404 page if (to.path ==
"/login") { next({ name: "index" }); } else { next(); } } } else { //
Redirect to login when no login status Or you can enter the no login state path if (to.path == "/login" || to.meta.auth === 0) {
next(); } else { next({ path: "/login" }); } } });
<> be careful
I don't need authentication to write the route here router Folder index.js, By routing meta information meta Carry the designated logo
{ path: "/err-404", name: "err404", meta: { authentication: false },
component: resolve => require(["../views/error/404.vue"], resolve) },
As mentioned above, the route is generated according to certain rules according to the background return menu data , So some are not menus , It also needs the route of login status , I wrote in router Folder router.js in , In the above steps 4 When processing the menu data returned from the background , And the processed menu route data are merged and passed together
addRoutes push into .
There is a certain risk of being invaded by the address bar , But the author here mostly is not too important route , If you ask for cough , You can set a dictionary to work with the background interface to accurately load each route .
// Join the enterprise { path: "/join-company", name: "join-company", component: resolve =>
require([`@/views/index/join-company.vue`], resolve) },
stay vuex Convert the assigned menu data into the front-end available routing data , That's what I did :
The management system needs to fill in a page address field when adding a new menu Url, After the front end gets the background menu data, it will Url
Field to match the path of the file loaded by the route , The advantage of one folder per menu is : You can split it here js,css And private components of this menu
menu.forEach(item => { let routerItem = { path: item.Url, name: item.Id,
meta: { auth: item.Children, }, // Routing meta information Parameters that can be carried when defining a route , Button operation permissions that can be used to manage each route
component: resolve => require([`@/views${item.Url}/index.vue`], resolve) //
Route map real view path }; routerBox.push(routerItem); });
This is what I do about how to precisely control every button , Put the button code in the routing meta information , Match under the current route to control whether the button on the page is created .
The menu data returns a multi-level structure , The subset under each menu is the button permission code array under the current menu , I put the buttons under each menu in the routing meta information of this menu meta.auth
in . The advantage of this is that : Button permission verification only needs to match the data under each menu route meta information , In this way, the length of the check pool will not exceed 5 individual .
created() { this.owner = this.$route.meta.auth.map(item => item.Code); }
methods: { matchingOwner(auth) { return this.owner.some(item => item === auth);
} }
demand 4 Auto update token, It's a simple judgment of time , And add a field in the request header to notify the background update token And return in the head , The front end receives the band token The request is updated directly token
// stay axios In the request interceptor of let token = getSession(auth_code); if (token)
config.headers.auth = token; if (tokenIsExpire(token)) { // Determine whether to refresh jwt
config.headers.refreshtoken = true; } // stay axios In response interceptor if (res.headers.auth) {
setSession(auth_code, res.headers.auth); }
For demand 5 It's troublesome to deal with it , To cross tab Page can only be accessed through cookie or local, I'm not allowed to use it here cookie So the localstorage. Read from a new page that opens
localstorage Internal token Data to synchronize account information of multiple pages .token Used jwt And front end md5 encryption .
Here we need to pay attention to the page switch to immediately synchronize the account information .
After demand 5 The global routing guard after transformation is like this :
function _AUTH_() { // Check whether the account number changes when switching windows
window.addEventListener("visibilitychange", function() { let Local_auth =
getLocal(auth_code, true); let Session_auth = getSession(auth_code); if
(document.hidden == false && Local_auth && Local_auth != Session_auth) {
setSession(auth_code, Local_auth, true); router.go(0) } })
router.beforeEach((to, from, next) => { // Judge whether the current user has pulled the permission menu if
(store.state.sidebar.userRouter.length === 0) { // Pull without menu getMenuRouter()
.then(res => { let _menu = res.data.Data.ColumnDataList || []; // if
(res.data.Data.ColumnDataList.length > 0) { // Organize the menu & Routing data
store.commit("setMenuRouter", _menu); // Push permission routing list
router.addRoutes(store.state.sidebar.userRouter); next({...to, replace: true
}); // } }) .catch(err => { // console.log(err); // Message.error(" Server connection failed ");
}); } else { // When you have user rights , Indicates that all accessible routes have been generated If you don't have permission to access the menu, it will enter automatically 404 page if (to.path ==
"/login") { next({ name: "index" }); } else { next(); } } } else { //
Redirect to login when no login status Or you can enter the no login state path if (to.path == "/login" || to.meta.auth === 0) {
next(); } else { next({ path: "/login" }); } } }); }
After demand 5 After transformation axios The request interceptor is like this , because ie unavailable visibilitychange, And try Baidu other properties invalid , As a result, rough handling was done before the request was made :
if (ie browser ) { setLocal('_ie', Math.random()) let Local_auth =
getLocal(auth_code, true); let Session_auth = getSession(auth_code); if
(Local_auth && Local_auth != Session_auth) { setSession(auth_code, Local_auth,
true); router.go(0) return false } }
Here's a small problem to pay attention to : Because it's used local Therefore, if you open the browser for the first time, you may be prompted that your login has expired , Here I believe that you can find your own solution
<> epilogue
After these simple and easy to use processing , A front-end authentication scheme with front-end and back-end separation was born
Technology