欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

服务端鉴权之Session/Cookie

程序员文章站 2024-01-29 18:08:04
...

Cookie

cookie原理
服务端在Header Set-Cookie,然后客户端会自动存取下来(cookie中可以看到)
下次请求,请求头会携带cookie传给后端

const http = require('http')
http
    .createServer((req,res)=>{
        if(req.url === '/favicon.ico'){
            res.end('')
            return
        }
        //观察cookie存在
        console.log('cookie:',req.headers.cookie)
        //设置cookie
        res.setHeader('Set-Cookie','cookie1=aaa')
        res.end('hello cookie')
    })
    .listen(3000)

存储在客户端
服务端鉴权之Session/Cookie
传送到服务端
服务端鉴权之Session/Cookie

cookie缺点

  1. 不安全,能在客户端看到(明文),可能被篡改(document.cookie = …)
  2. 容量限制,只能存很小的数据
  3. 不能存实例

因为有上面的缺点,不适合做鉴权,需要用session来配合

Session

session原理
浏览器首次访问,生成uid保存在服务端session中,且将uid存储于cookie
下次访问,根据uid对请求进行认证

const http = require('http')
const session = {}
http
    .createServer((req,res)=>{
        //观察cookie是否存在
        console.log('cookie',req.headers.cookie)
        //存储一对键值对
        const sessionKey = 'uid'
        const cookie = req.headers.cookie
        if(cookie&&cookie.indexOf(sessionKey)!=-1){
            //下次访问
            res.end('Come Back')
            //使用正则解析是否存在之前的cookie
            const pattern = new RegExp(`${sessionKey}=([^;]+);?\s*`)
            const uid = pattern.exec(cookie)[1]
            console.log('session',uid,session,session[uid])
        }else{
            //第一次访问
            //设置键值对的值,用你需要的规则,这儿用随机数代替
            const uid = (Math.random()*9999999999).toFixed()
            res.setHeader('Set-Cookie',`${sessionKey}=${uid};`)
            //真正的数据
            session[uid] = {name:'admin'}
            res.end('hello admin')
        }
        res.end('hello session') 
    })
    .listen(3000)

服务端鉴权之Session/Cookie
服务端鉴权之Session/Cookie

session优点

  1. 浏览器只存了一个编号或加密串,很难推定
  2. 真正的数据,存储于服务端,安全可存实例又无容量限制,可进行鉴权

Koa中实现Session

通过koa-session中间件来实现

const koa = require('koa')
const app = new koa()
const session = require('koa-session')
//签名key keys作用 用来对cookie进行签名
app.keys = ['some secret']
//配置项
const SESS_CONFIG = {
    key: "sess", //cookie链名
    maxAge: 86400000, // 有效期,默认一天
    httpOnly: true, // 只能用于http传输,仅服务器修改,提高安全性
    signed: true // 签名cookie,如秘钥
}
//注册
app.use(session(SESS_CONFIG, app))
//测试
app.use(ctx => {
    if(ctx.path === '/favicon.ico') return
    //获取
    let n = ctx.session.count || 0
    //设置
    ctx.session.count = ++n
    ctx.body = '第'+n+'次访问'
})
app.listen(3000)

服务端鉴权之Session/Cookie

上面的session存储于变量(内存)中,可能会遇到下面的一些问题

  1. 内存容量不足
  2. 无法满足横向扩展(node配置于多台服务器,无法保证下次进来还是上一台服务器)

所以通常情况下需要将session进行全局存储,一般将其存在redis中

redis存储session

redis介绍
一个高性能的key-value数据库
redis缓存产品的三个特点

  • 支持数据持久化,可以将内存中的数据保存在磁盘中,重启的适合可以再次加载进行使用
  • 不仅支持简单的key-value类型的数据,同时还提供list、set、zset、hash等数据结构的存储
  • 支持数据的备份,即master-slave模式的数据备份
    redis优势
  • 性能极高,由C语言编写,能读的速度是110000次/s,写的速度是81000次/s
  • 丰富的数据类型,支持二进制案例的Strings、Lists、Hashes、Sets及Ordered Sets数据类型操作
  • 所有操作都是原子性的,即要么成功执行要么失败完全不执行。单个操作是原子性的,多个操作也支持事务,通过MULTI和EXEC指令包起来
  • 丰富的特性,支持publish/subscribe,通知,key过期等特性
    redis使用
    1、github下载
    2、解压改名为redis
    3、reids-server启动
    redis测试
const redis = require('redis')
//连接redis
const client = redis.createClient(6379,'localhost')
//使用事件发射器,检测错误
client.on("error", function (err) {
    console.log("Error " + err);
});
//设置
client.set('key','value')
//拿取
client.get('key',(err,v)=>{
    if(err) throw err;
    console.log('redis get ', v)
})

如果报下面的错误,则表示reids-server未启动
Error Error: Redis connection to localhost:6379 failed - connect ECONNREFUSED 127.0.0.1:6379

koa-redis使用
安装
npm i -S koa-redis

const koa = require('koa')
const app = new koa()
const session = require('koa-session')
const redisStore = require('koa-redis')
const redis = require('redis')
const redisClient = redis.createClient(6379,'localhost')
const wrapper = require('co-redis')
const client = wrapper(redisClient)
//签名key keys作用 用来对cookie进行签名
app.keys = ['some secret']
//配置项
const SESS_CONFIG = {
    key: "sess", //cookie链名
    maxAge: 86400000, // 有效期,默认一天
    httpOnly: true, // 仅服务器修改
    signed: true, // 签名cookie
    store: redisStore({client}) // 指定存储则会按照你要求的去存储,否则会默认存储到内存中
}
//注册
app.use(session(SESS_CONFIG, app))
app.use(async (ctx,next) => {
    const keys = await client.keys('*')
    keys.forEach(async key => console.log(await client.get(key)))
    await next()
})
//测试
app.use(ctx => {
    if(ctx.path === '/favicon.ico') return
    //获取
    let n = ctx.session.count || 0
    //设置
    ctx.session.count = ++n
    ctx.body = '第'+n+'次访问'
})
app.listen(3000)

服务端鉴权之Session/Cookie

session/cookie鉴权的缺点

1、服务端有状态,需要去进行相关存储,若服务器断了,就会刷新内存,重连之后下次请求就会出现问题
2、基于cookie传送,cookie是基于浏览器的,不够灵活。如APP、跨域等其他情况会受限