如何通过cookie、session鉴权(nodejs/koa)

一颗冰淇淋

http是一种无状态的协议,每一个请求都是独立的,即使同一个页面向服务器发送多次请求,服务器也无法区分是不是同一用户,所以这个时候可以借助于cookie来做身份认证,当用户登录成功,服务器为浏览器设置cookie,而在后面的请求当中,浏览器发送请求的时候,就会携带cookie,需注意的是,cookie需要同源的请求下才会发送。

设置cookie的时候还有以下值属性可以进行选择,分别是过期时间和作用域,没有设置过期时间,cookie的生命周期仅为会话阶段,关闭浏览器即被删除,通过max-age或者expires设置,cookie的生命周期为有效期内,超出有效期则被删除,作用域可设置domain和path,domain代表指定哪些主机可以接收cookie,没有设置的时候默认origin,仅当前域名有效,设置后当前域名和子域名都可用,path用于指定主机下哪个路径可以接受cookie。

浏览器端通过 document.cookie 来获取和设置cookie,当需要在浏览器使cookie失效,则可以设置 max-age=0

document.cookie = 'name=alice;max-age=5' // max-age在浏览器端的单位为秒

在服务器端,通过nodejs中http模块设置响应头信息setCookie,第二次请求则会在请求头中携带cookie,如果对nodejs不太熟悉,可以参考这一篇文章, nodejs中如何使用http创建一个服务

const http = require('http')
const server = http.createServer((req, res) => {
  res.setHeader('Set-Cookie', ['age=16;max-age=20000']) 
  // max-age在nodejs的单位为毫秒
  res.end('cookie')
})

koa中通过 ctx.cookies.set和ctx.cookies.get来分别设置和获取cookie,以下代码表示,当访问 localhost:7000/login的时候,服务器为浏览器设置一个 cookie,当访问 localhost:7000/user的时候,服务器获取浏览器的cookie。

如果对koa还不太熟悉,可以参考这一篇文章,让nodejs开启服务更简单--koa篇

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

router.get('/login', (ctx, next) => {
  ctx.cookies.set('wheater', 'sunny', {
    maxAge: 60*1000
  })
  ctx.body = 'set cookie'
})

router.get('/user', (ctx, next) => {
  const cookie = ctx.cookies.get('wheater')
  ctx.body = cookie
})

app.use(router.routes())
app.use(router.allowedMethods())
app.listen(7000, ()=>{console.log('开启服务')})

使用nodejs中的http模块对请求进行响应需要自己处理复杂逻辑,通过 koa 可以简化很多操作,比如koa就自动加上了 HttpOnly的属性

以上存储在cookie中的数据都是以明文的形式,非常的不安全,真正在实际应用时,需要将用户信息进行加密处理,这时候可以考虑使用 session,session也是给浏览器填充cookie值,但它可以为填充的cookie进行加密处理

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

// signed: true 增加一个sessionid的签名文件 sessionid.sig
const session = Session({
  key: 'sessionId',
  maxAge: 60 * 1000,
  signed: true
}, app)
app.keys = ['alice']
app.use(session)

router.get('/login', (ctx, next) => {
  const id = 1;
  const name = 'lucy'
  ctx.session.user = { id, name }
  ctx.body = 'set session'
})

router.get('/user', (ctx, next) => {
  ctx.body = ctx.session.user   // { id: 1, name: 'lucy'}
})

app.use(router.routes())
app.use(router.allowedMethods())
app.listen(7000, () => { console.log('开启服务') })

session以base64编码的形式进行存储,可以被解码,也并不完全安全,所以通过 signed: true 设置密钥,使增加一个加密后的 sessionid.sig,这样服务器在获取cookie中设置的session数据就会对数据的真实性进行校验

以上cookie和session可以用于身份校验,但这种方式存在以下问题:
1、cookie在每个请求的时候都会被携带,无形之中增加了带宽
2、cookie只有4kb的容量,对于复杂需求可能不够用
3、在浏览器外的其他客户端,cookie并不会主动携带,需要手动设置cookie和session,所以不同端在处理cookie上存在差异
4、 对于分布式系统和服务器集群中如何可以保证其他系统也可以正确的解析session也存在问题,像java中每台服务器获取session的密钥都不一样

基于以上问题,目前越来越多的开发者会使用 token 来进行鉴权,下一篇文章中仔细介绍 token 如何做到身份校验~

阅读 298

hello

30 声望
1 粉丝
0 条评论
你知道吗?

hello

30 声望
1 粉丝
宣传栏