HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。
cookie
-
cookie 存储在客户端。 cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
-
默认情况下不可跨域传递 cookie,如需跨域传递 cookie 需要在代码中手动配置。
nodejs操作cookie
const http = require('http')
const server = http.createServer((req, res) => {
// 设置 cookie
// res.setHeader('Set-Cookie', 'a=100') // 设置单条cookie
res.setHeader('Set-Cookie', ['a=100', 'b=200']) // 设置多条cookie
if (req.headers.cookie) { // 如果请求头中有cookie
// 获取 cookie
const cookieStr = req.headers.cookie
console.log('request cookie:', cookieStr)
// 结构化 cookie
// cookieStr: 'a=100; b=200' -> { a: '100', b: '200' }
const cookieObj = {}
cookieStr.split(';').forEach(item => {
const tempArr = item.trim().split('=')
const key = tempArr[0] // 'a'
const val = tempArr[1] // '100'
cookieObj[key] = val
})
console.log('request cookie object:', cookieObj)
}
res.end('cookie test')
})
server.listen(3000)
console.log('server listening on 3000 port')
浏览器非第一次访问(第一次访问时Request Headers
中没有Cookie):
![](https://i-blog.csdnimg.cn/blog_migrate/1d207025ef22b197cac07bee351c194d.png)
终端打印:
![](https://i-blog.csdnimg.cn/blog_migrate/b03c05224ffb5254c3a67a8b59771f3f.png)
Koa2操作cookie
const Koa = require('koa')
const app = new Koa()
app.use(async(ctx) => {
// 设置 cookie
ctx.cookies.set('a', '100')
// Response Headers中:Set-Cookie: a=100; path=/; httponly
// 获取 cookie
console.log('request cookie a:', ctx.cookies.get('a'))
// 浏览器非第一次访问,终端打印:request cookie a: 100
// 结构化,koa2 已经做好了
ctx.body = 'cookie test by Koa2'
})
app.listen(3000)
session
一方面,cookie 的容量有限,并且会随着每次请求发送到服务端,不适合存储太多信息;另一方面如果直接将信息存储在cookie 中也比较危险。所以通常使用 cookie 存储一个标识(比如 sessionId),与标识对应的信息存储在服务器端的session 中。使用cookie-session
实现登录校验的大致流程如下图。
![](https://i-blog.csdnimg.cn/blog_migrate/659778b1aa5b1f4a055ebb07d193347f.png)
注:下述代码中,使用ctx.session
拿到的是与前端Request Headers
中传过来的Cookie中的sessionID对应的session,即根据当前传过来的Cookie中的sessionID去寻找的对应的session。
session的基本使用
在使用koa框架的脚手架koa-generator
创建的koa项目中,安装npm包koa-generic-session
:
npm i koa-generic-session
在routes文件夹下新建文件sessionDemo.js
,代码如下:
const router = require('koa-router')()
// 记录用户对/session-test网页的访问次数
router.get('/session-test', async (ctx, next) => {
// 使用ctx.session拿到的是与前端Request Headers中传过来的cookie中的sessionID对应的session
// 同一浏览器不同标签页访问同一路径时cookie是共享的,不同浏览器访问同一路径时cookie是不同的
// 所以同一浏览器不同标签页访问当前路径得到的viewCount一致,不同浏览器访问当前路径得到的viewCount相互独立
ctx.session.viewCount = ctx.session.viewCount ?? 0
ctx.session.viewCount++
ctx.body = `viewCount: ${ctx.session.viewCount}`
console.log(ctx.session)
})
module.exports = router
app.js
文件中加入如下代码:
const session = require('koa-generic-session')
// 引入路由
const sessionDemo = require('./routes/sessionDemo')
// 使用koa-generic-session则不用手动发送cookie,其自动配置了cookie和session的对应关系
app.use(session({
cookie: { // 配置cookie
path: '/', // cookie在根路径下的所有路径都有效
maxAge: 1000 * 60 * 60 * 24, // cookie过期时间,单位毫秒
httpOnly: true, // cookie只允许服务端操作
}
}))
// 对cookie中的sessionID进行加密的密钥,可以自定
app.keys = ['abc123.!@#']
// 注册路由
app.use(sessionDemo.routes(), sessionDemo.allowedMethods())
启动服务:npm run dev
浏览器访问urlhttp://localhost:3000/session-test
:
![](https://i-blog.csdnimg.cn/blog_migrate/c23b522b041ee160dcca4bc93aa8e20e.png)
终端打印:
![](https://i-blog.csdnimg.cn/blog_migrate/9fd0522177b17fa6c473fda67310474f.png)
session登录与校验
典型的session登陆/验证流程如下:
koa-generic-session
自动配置了cookie和session的对应关系,当向ctx.session
中进行set操作时,会向响应头中添加Set-Cookie字段;对ctx.session
进行get操作则不会向响应头中添加Set-Cookie字段。- 在登陆成功后存session是为了表明:1.已经成功登录;2.登录的是哪个用户。是为了在校验的时候使用(对于需要登录才能使用的功能,要校验用户是否登录)。
登录
注:下述代码为在上述代码的基础上完成的
在routes文件夹下新建文件login.js
,代码如下:
const router = require('koa-router')()
const User = require('../db/model')
// 此处为方便测试,使用GET请求进行登录,正常应该使用POST请求
router.get('/login', async(ctx, next) => {
const { username, password } = ctx.query
// 如果使用POST请求,则要从ctx.request.body中获取username与password
if (username === undefined || password === undefined) {
ctx.body = { errno: -1, message: '请输入用户名与密码' }
return
}
const user = await User.findOne({ // 查询数据库
username: username,
password: password,
})
if (user) {
// 登陆成功
ctx.session.userInfo = user // 执行此语句则会向session中添加userInfo,并会向响应头中添加Set-Cookie字段,向前端返回sessionID
ctx.body = { errno: 0, message: '登陆成功', data: user }
} else {
// 登陆失败
ctx.body = { errno: -1, message: '用户名或密码错误'}
}
})
module.exports = router
app.js
中加入如下代码:
// 引入路由
const login = require('./routes/login')
// 注册路由
app.use(login.routes(), login.allowedMethods())
启动服务后,浏览器访问:
![](https://i-blog.csdnimg.cn/blog_migrate/891bb2b61a3cef07ce2dfcc47c875edf.gif)
校验
注:下述代码为在上述代码的基础上完成的
新建middlewares文件夹,其中新建loginCheck.js
文件,代码如下:
// 定义中间件
async function loginCheck(ctx, next) {
if (ctx.session?.userInfo) {
await next()
} else {
ctx.body = { errno: -1, message: '尚未登陆' }
}
}
// 导出中间件
module.exports = loginCheck
在需要登录才能使用的功能中加入以上登录校验中间件。比如上述sessionDemo.js
中的记录用户访问次数的功能:
const router = require('koa-router')()
const loginCheck = require('../middlewares/loginCheck')
// 记录用户对/session-test网页的访问次数
router.get('/session-test', loginCheck, async (ctx, next) => { // 加入登录校验中间件
ctx.session.viewCount = ctx.session.viewCount ?? 0
ctx.session.viewCount++
ctx.body = `viewCount: ${ctx.session.viewCount}`
})
module.exports = router
启动服务后,浏览器访问:
![](https://i-blog.csdnimg.cn/blog_migrate/7e66fc45d6435b08782062d25eed3c06.gif)
至此,使用cookie-session进行登录及校验相关的内容就介绍完了,希望对大家有所帮助~
如有疏漏之处欢迎评论区留言指正哦~