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 相关文章推荐
学习YUI.Ext基础第一天
Mar 10 Javascript
JavaScript 基础篇(一)
Mar 30 Javascript
jQuery使用load()方法载入另外一个网页文件内的指定标签内容到div标签的方法
Mar 25 Javascript
在Node.js应用中读写Redis数据库的简单方法
Jun 30 Javascript
jQuery实现多级联动下拉列表查询框
Jan 18 Javascript
简单实现Bootstrap标签页
Aug 09 Javascript
基于JavaScript实现熔岩灯效果导航菜单
Jan 04 Javascript
JS 调试中常见的报错问题解决方法
May 20 Javascript
Easyui和zTree两种方式分别实现树形下拉框
Aug 04 Javascript
angularJS的radio实现单项二选一的使用方法
Feb 28 Javascript
vue.js 双层嵌套for遍历的方法详解, 类似php foreach()
Sep 07 Javascript
JavaScript生成一个不重复的ID的方法示例
Sep 16 Javascript
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
php 移除数组重复元素的一点说明
2008/11/27 PHP
用php的ob_start来生成静态页面的方法分析
2011/03/09 PHP
php从完整文件路径中分离文件目录和文件名的方法
2015/03/13 PHP
php each 返回数组中当前的键值对并将数组指针向前移动一步实例
2016/11/22 PHP
Javascript的闭包
2009/12/31 Javascript
js弹出层永远居中实现思路及代码
2013/11/29 Javascript
使用CSS+JavaScript或纯js实现半透明遮罩效果的实例分享
2016/05/09 Javascript
AngularJs Javascript MVC 框架
2016/06/20 Javascript
全面介绍javascript实用技巧及单竖杠
2016/07/18 Javascript
jQuery学习笔记之回调函数
2016/08/15 Javascript
让浏览器崩溃的12行JS代码(DoS攻击分析及防御)
2016/10/10 Javascript
javascript按钮禁用和启用的效果实例代码
2017/10/29 Javascript
Angular4集成ng2-file-upload的上传组件
2018/03/14 Javascript
vue和webpack安装命令详解
2018/06/15 Javascript
ES6 Promise对象概念及用法实例详解
2019/10/15 Javascript
python进阶教程之动态类型详解
2014/08/30 Python
零基础写python爬虫之抓取百度贴吧代码分享
2014/11/06 Python
详解Python字符串对象的实现
2015/12/24 Python
详解Python编程中基本的数学计算使用
2016/02/04 Python
Python减少循环层次和缩进的技巧分析
2016/03/15 Python
python爬虫入门教程--利用requests构建知乎API(三)
2017/05/25 Python
linux环境下python中MySQLdb模块的安装方法
2017/06/16 Python
python实现单链表中删除倒数第K个节点的方法
2018/09/28 Python
tensorflow 20:搭网络,导出模型,运行模型的实例
2020/05/26 Python
HTML5实现分享到微信好友朋友圈QQ好友QQ空间微博二维码功能
2018/01/03 HTML / CSS
HTML5中如何显示视频呢 HTML5视频播放demo
2013/06/08 HTML / CSS
纽约21世纪百货官网:Century 21
2016/08/27 全球购物
中专自我鉴定范文
2013/10/16 职场文书
《掌声》教学反思
2014/02/23 职场文书
企业年度评优方案
2014/06/02 职场文书
学校清明节活动总结
2014/07/04 职场文书
房产协议书范本
2014/10/18 职场文书
安全生产工作汇报
2014/10/28 职场文书
2016优秀教师先进个人事迹材料
2016/02/25 职场文书
Html5生成验证码的示例代码
2021/05/10 Javascript
Vue Element UI自定义描述列表组件
2021/05/18 Vue.js