MrSunOcean 2019-06-25
本文来谈谈前后端分离的项目如何做权限控制和自动登陆,如何应用history模式使网址看起来和传统无差,以及设置404页面,以vue为例。
自动登陆一般是通过cookie和session的配合实现的。原理是登陆完成后服务端将用户信息保存在session中,并将sessionid保存在cookie中发送给客户端。客户端将cookie存起来,下次访问时带上cookie(如果没有过期)发起请求,服务端收到请求从cookie里获取sessionid来找到session,如果session还在的话就认为此用户不需要登陆了,给他返回他需要的数据。否则就跳转到登陆页面。
看起来好像很复杂,实际应用起来却十分简单,因为前人已经为我们做了很多工作,很多框架里使用session便会自动生成cookie发往客户端,客户端(浏览器)会自动保存cookie(没有禁用cookie的话),下次发送请求的时候客户端也会自动带上cookie,服务端的session包会自动从相关cookie的信息里找到session。很多工作库和浏览器已经帮我们做了,其实我们只需要做两个事:1、存session 2、判断session是否有效。
我们通过session和cookie的过期时间来设置自动登陆的有效期,一般两者设置相同的时间,因为cookie很容易伪造。session的储存位置也很有讲究,可以储存在文件系统里、内存里、redis,单台服务器存在文件系统里就行了,分布式系统的话就得用redis了。
原理讲完了,下面说应用。传统后端项目自动登陆没前端啥事,但在前后端分离的项目里,控制路由跳转的逻辑都在前端,前后端通过接口来交流,我们只需要在首次进入前端单页应用时给后端发送一个请求,带上cookie让后端返回是否登陆,如果后端返回为403则前端控制跳转登陆页面。
并不是所有页面都是开放的,有些页面需要登陆后或者一些权限才能使用,这时候就需要用到权限控制,这个工作在前后端分离的项目里,前后端都要做。
先说前端,因为有些接口需要用户id等信息,所以我们需要用到vuex来储存全局数据,同时配合vue-router的meta元数据和钩子实现权限控制。vue-router文档里有介绍。
具体应用就是给需要权限的路由加上mete字段,在进入每个路由前调用钩子,如果需要权限并且没有vuex里user信息,就发起检查登陆请求,如果已登陆这个请求会返回用户信息并存在vuex里,否则返回403错误码前端跳转登陆。这样前端只需要进入页面检查一次登陆就行了,需要用户信息的接口去vuex取即可,必要时可以手动刷新user数据。当然刷新页面后vuex便清空了,需要重新检查登陆,有需要的话可以储存在localstorage或cookie里。注销的时候调用注销接口后端清除session,前端清除user
代码片段:
// router.js router文件里 routes: [ { path: '/login', component: Foo, }, { path: '/user', component: Foo, meta: { requiresAuth: true } }, ] router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.auth !== false) && !store.state.user) { store.dispatch('checkLogin', next); } else { next(); } }); // store.js vuex文件里 actions: { // 检查登陆 checkLogin({commit}, next) { axios('get', '/check-auth') .then(res => { if (res.status == 200) { commit('user', res.data.data); next(); } }) .catch(err => { commit('user', null); next('/login'); }) }, }
虽然前端做了权限控制,但是后端也需要做接口权限控制,防止接口被滥用。web安全是一个很大的话题,本人对这方面了解的不是很深入,只能浅显的谈谈。比如应用https保证传输安全;请求时带上xrsf-token;服务端对Access-Control-Allow-Origin做限制,防止跨域请求。如果是授权的话使用oauth2.0方案,如果是简单的基于登陆的应用,在后端写一个中间件,需要登陆权限的接口都走这个中间件,未登陆便不返回数据。
什么是history模式?vue-router文档里介绍的比较清楚了
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
history 模式利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!
vue等单页应用的网址是通过hash模拟实现的,其实是网址加上#path,这样不光很丑陋还占用了锚点,而且在很多请求中#hash部分会被忽略,例如微信开发中。react-router就提到,每个人都应该使用history模式。
开启history模式需要后端配置,一般是在web代理服务器上配置,也可以通过后端路由来重定向。例如用的较多的ngnix配置:
location / { try_files $uri $uri/ /index.html; }
配置很简单,这配置是什么意思?为什么需要这么做呢?如果http://yoursite.com指向你的单页的index.html这个文件,那会打开这个html并运行其中的js,你的应用就呈现给用户了。但是你访问http://yoursite.com/user,这个网址并没有指向静态文件,也没有后端路由去处理他,就会出现404,浏览器根本找不到你的单页入口。通过这个配置,访问http://yoursite.com/user服务器尝试寻找index.html入口文件,在浏览器拿到html和js运行后,vue-router会分析url之中的path:/user,利用js控制理由渲染user页面。
用户总会不小心误入歧途,所以你需要一个404页面带她回家。配置404页面很简单,就是写一个路由匹配所有路径(*),放在最后,匹配不到的都会进入404页面。
代码片段:
// router.js router文件里 routes: [ { path: '/login', component: Foo, }, { path: '/user', component: Foo, meta: { requiresAuth: true } }, // 一定要在最后 { path: '*', component: 404, meta: { requiresAuth: true } }, ]