vue+koa2实现session、token登陆状态验证的示例


Posted in Javascript onAugust 30, 2019

Session 登陆与 Token 登陆的区别

1、Session 登陆是在服务器端生成用户相关 session 数据,发给客户端 session_id 存放到 cookie 中,这样在客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户认证。这种认证方式,可以更好的在服务端对会话进行控制,安全性比较高(session_id 随机),但是服务端需要存储 session 数据(如内存或数据库),这样无疑增加维护成本和减弱可扩展性(多台服务器)。 CSRF 攻击一般基于 cookie。另外,如果是原生 app 使用这种服务接口,因为没有浏览器 cookie 功能,所以接入会相对麻烦。

2、基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用户验证后,服务端生成一个 token(hash 或 encrypt)发给客户端,客户端可以放到 cookie 或 localStorage 中,每次请求时在 Header 中带上 token,服务端收到 token,通过验证后即可确认用户身份。这种方式相对 cookie 的认证方式就简单一些,服务端不用存储认证数据,易维护扩展性强,token 存在 localStorage 可避免 CSRF,web 和 app 应用都比较简单。不过这种方式在加密或解密的时候会有一些性能开销(好像也不是很大),有些对称加密存在安全隐患(aes cbc 字节翻转攻击)。

koa + session 登陆验证

1、首先要安装 koa-sisson 包

npm install koa-session -S

koa-session 实际上是通过 cookie 来保存信息的。koa-session 在服务器上生成一个信息,通过加密后,以 cookie 的形式发送到用户的浏览器,在用户浏览器上可以看到是一个加密的 cookie 字段,然后在服务端路由中通过 ctx.session.xx 来获取 xx 这个信息。而当每次当用户发送请求时候,就可以获取到这个 cookie,koa-sesscion 会内部会帮我们解密为最初的存储的信息,于是我们可以通过判断这个 cookie 是存在来校验用户是否已经登录了。

2、app.js 中初始化

const Koa = require('koa')
const app = new Koa()
const session = require('koa-session')
const bodyParser = require('koa-bodyparser')
const Router = require('koa-router')
const router = new Router()

const CONFIG = {
 key: 'koa:sess', /** (string) cookie key (default is koa:sess) cookie 的Name */
 /** (number || 'session') maxAge in ms (default is 1 days) */
 /** 'session' will result in a cookie that expires when session/browser is closed */
 /** Warning: If a session cookie is stolen, this cookie will never expire */
 maxAge: 86400000, /** cookie 的过期时间 */
 autoCommit: true, /** (boolean) automatically commit headers (default true) */
 overwrite: true, /** (boolean) can overwrite or not (default true) */
 httpOnly: true, /** (boolean) httpOnly or not (default true) */
 signed: true, /** (boolean) signed or not (default true) */
 rolling: false, /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. (default is false) */
 renew: false, /** (boolean) renew session when session is nearly expired, so we can always keep user logged in. (default is false)*/
}
app.keys = ['login secret'] // 加密密钥
app.use(session(CONFIG, app));

app.use(bodyParser())
app.use(router.routes()).use(router.allowedMethods())

3、登陆路由

router.post('/login', async (ctx) => {
 try {
 const data = ctx.request.body.data
 const { username, password } = data
 if (true) {
  // 保存登录状态,这句代码会在浏览器中生成一个以 "koa:sess" 为 Name 的 cookie
  ctx.session.userInfo = {username: '', userID: ''}
  ctx.body = {code: 1, message: '登陆成功'}
 } else {
  ctx.body = {code: 0, message: '账号或密码错误'}
 }
 } catch(err) {
 throw new Error(err)
 }
})

// 前端
axios.post('/login', {username: '', password: ''}).then(res => {})

4、校验是否已登陆

router.get('/getSession', async (ctx) => {
 try {
 if (ctx.session.userInfo) {
  ctx.body = {code: 1, message: '已登陆'}
 } else {
  ctx.body = {code: 0, message: '未登陆'}
  // 跳转到登录页
  // ctx.response.redirect('/login') 
 }
 } catch(err) {
 throw new Error(err)
 }
})

// 前端
axios.get('/getSession').then(res => {})

5、退出登陆

router.post('/logout', async (ctx) => {
 try {
 // 将登录信息清空
 ctx.session = null
 // 跳转到登录页或网站首页
 ctx.response.redirect('/')
 } catch(err) {
 throw new Error(err)
 }
})

// 前端
axios.post('/logout').then(res => {})

koa + token 登陆验证

1、安装 jsonwebtoken 包

npm install jsonwebtoken -S

2、app.js 初始化

const Koa = require('koa')
const app = new Koa()
const jwt = require('jsonwebtoken')
const bodyParser = require('koa-bodyparser')
const Router = require('koa-router')
const router = new Router()
const tokenConfig = {privateKey: 'xxxxxxxxxxxx'} // 加密密钥

app.use(bodyParser())
app.use(router.routes()).use(router.allowedMethods())

3、登陆路由

router.post('/login', async (ctx) => {
 try {
 const data = ctx.request.body.data
 const { username, password } = data
 if (true) {
  const userInfo = {username: '', userID: ''}
  const token = jwt.sign(userInfo, tokenConfig.privateKey, {expiresIn: '7d'}) // 签发 token, 7天有效期
  ctx.body = {code: 1, message: '登陆成功', data: {token: 'Bearer ' + token}}
 } else {
  ctx.body = {code: 0, message: '账号或密码错误'}
 }
 } catch(err) {
 throw new Error(err)
 }
})

前端登陆

axios.post('/login', {username: '', password: ''}).then(res => {
 if (res.data.code === 1) {
 localStorage.setItem('token', res.data.data.token)
 // vuex 存储 userInfo 和登陆状态
 store.commit('SET_USERINFO', {userInfo: res.data.data.userInfo, status: true})
 }
})

4、校验是否已登陆

router.get('/getUserInfo', async (ctx) => {
 try {
 const token = ctx.get('Authorization') // 获取请求 Header 中 Authorization 值
 let userInfo = {}
 if (token === '') {
  ctx.body = {code: 0, message: '未登陆'}
 } else {
  try {
  userInfo = jwt.verify(token.split(' ')[1], tokenConfig.privateKey) // 验证 token
  ctx.body = {code: 1, message: '已登陆', data: {userInfo: userInfo: loginStatus: true}}
  } catch(err) {
  // token 过期或无效
  ctx.body = {code: 0, message: '未登陆', data: {userInfo: {}: loginStatus: false}}}
  }
 }
 } catch(err) {
 throw new Error(err)
 }
})

要每次的请求中都带上 token 信息,要给 axios 设置请求拦截

// 请求拦截,在每次请求中的 header 中带上 token
axios.interceptors.request.use(config => {
 let token = localStorage.getItem('token')
 if (token) {
 config.headers.common.Authorization = token
 }
 return config
}, error => {
 return Promise.reject(error);
})

每次进入页面之前要判断下是否已登陆,是否有权限进入该页面,之前我是在每个页面的 created 钩子函数中去请求 '/getUserInfo' 判断是否以登陆,这样做繁琐,并且页面会先呈现一下,然后一闪而过(验证不过的情况下),在路由钩子函数中可全局配置

// 路由守卫, 在跳转之前执行
router.beforeEach((to, from, next) => {
 let token = localStorage.getItem('token')
 let requireAuth = to.meta.requireAuth // VueRouter 里配置页面是否需要登陆进入
 let root = to.meta.root // VueRouter 里配置页面是否需要登陆且管理员权限进入
 if (!token) {
 // vuex 清除 userInfo 和登陆状态
 store.commit('SET_USERINFO', {userInfo: {}, status: false})
 requireAuth ? next({path: '/'}) : next()
 } else {
 axios.get(API.getUserInfo).then(res => {
  // vuex 存储 userInfo 和登陆状态
  store.commit('SET_USERINFO', {userInfo: res.data.userInfo, status: res.data.loginStatus})
  if (requireAuth) {
  if (!res.data.loginStatus || (root && !res.data.userInfo.root)) {
   next({path: '/'})
  } else {
   next()
  }
  } else {
  next()
  }
 })
 }
})

/** VueRouter
{
 path: '/admin',
 name: 'admin',
 meta: {
 requireAuth: true,
 root: true
 },
}
*/

5、退出登陆

因为服务器端并没有存储用户登陆相关信息,只与前端是否存在 token 或是否能验证通过有关,所以退出登陆就将 token 清除即可

methods: {
 logout() {
 localStorage.removeItem('token')
 // vuex 清除登陆信息
 store.commit('SET_USERINFO', {userInfo: {}, status: false})
 if (this.$route.path !== '/') {
  this.$router.push({path: '/'})
 }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
ajax中get和post的说明及使用与区别
Dec 23 Javascript
面向对象继承实例(a如何继承b问题)(自写)
Jul 01 Javascript
将Datatable转化成json发送前台实现思路
Sep 06 Javascript
以JSON形式将JS中Array对象数组传至后台的方法
Jan 06 Javascript
JS获取下拉列表所选中的TEXT和Value的实现代码
Jan 11 Javascript
Jquery中ajax方法data参数的用法小结
Feb 12 Javascript
Javascript使用SWFUpload进行多文件上传
Nov 16 Javascript
JavaScript正则获取地址栏中参数的方法
Mar 02 Javascript
Vue.js计算机属性computed和methods方法详解
Oct 12 Javascript
在vue中使用Echarts利用watch做动态数据渲染操作
Jul 20 Javascript
Openlayers绘制聚合标注
Sep 28 Javascript
在vue中import()语法不能传入变量的问题及解决
Apr 01 Vue.js
js利用递归与promise 按顺序请求数据的方法
Aug 30 #Javascript
Vue-CLI 项目在pycharm中配置方法
Aug 30 #Javascript
JS实现页面跳转与刷新的方法汇总
Aug 30 #Javascript
Vue 动态组件components和v-once指令的实现
Aug 30 #Javascript
java实现单链表增删改查的实例代码详解
Aug 30 #Javascript
vuex vue简单使用知识点总结
Aug 29 #Javascript
js中的this的指向问题详解
Aug 29 #Javascript
You might like
业余方法DIY电子管FM收音机
2021/03/02 无线电
发挥语言的威力--融合PHP与ASP
2006/10/09 PHP
PHP5中的时间相差8小时的解决办法
2008/03/28 PHP
在PHP中使用模板的方法
2008/05/24 PHP
php shell超强免杀、减少体积工具实现代码
2012/10/16 PHP
php随机取mysql记录方法小结
2014/12/27 PHP
CentOS 安装 PHP5.5+Redis+XDebug+Nginx+MySQL全纪录
2015/03/25 PHP
zen cart实现订单中增加paypal中预留电话的方法
2016/07/12 PHP
javascript 当前日期加(天、周、月、年)
2009/08/09 Javascript
写出更好的JavaScript之undefined篇(上)
2009/11/22 Javascript
有关javascript的性能优化 (repaint和reflow)
2013/04/12 Javascript
javascript中的onkeyup和onkeydown区别介绍
2013/04/28 Javascript
JavaScript限定复选框的选择个数示例代码
2013/08/25 Javascript
jquery easyui使用心得
2014/07/07 Javascript
jQuery中attr()和prop()在修改checked属性时的区别
2014/07/18 Javascript
JQuery中属性过滤选择器用法实例分析
2015/05/18 Javascript
使用Angular和Nodejs、socket.io搭建聊天室及多人聊天室
2015/08/21 NodeJs
JavaScript必看小技巧(必看)
2016/06/07 Javascript
详细谈谈javascript的对象
2016/07/31 Javascript
js获取上传文件的绝对路径实现方法
2016/08/02 Javascript
详解webpack + react + react-router 如何实现懒加载
2017/11/20 Javascript
浅谈实现vue2.0响应式的基本思路
2018/02/13 Javascript
JS+DIV实现拖动效果
2020/02/11 Javascript
JavaScript中this函数使用实例解析
2020/02/21 Javascript
详解python3百度指数抓取实例
2016/12/12 Python
简单谈谈python中的语句和语法
2017/08/10 Python
itchat接口使用示例
2017/10/23 Python
Python利用splinter实现浏览器自动化操作方法
2018/05/11 Python
Python TestCase中的断言方法介绍
2019/05/02 Python
Python @property装饰器原理解析
2020/01/22 Python
numpy实现RNN原理实现
2021/03/02 Python
DIY蛋糕店的创业计划书范文
2013/12/26 职场文书
大学毕业寄语大全
2014/04/10 职场文书
《桃林那间小木屋》教学反思
2014/05/01 职场文书
保护动物的宣传语
2015/07/13 职场文书
小学生红领巾广播稿
2015/08/19 职场文书