cookie
解析cookie
req.cookie = {}
const cookieStr = req.headers.cookie || '' // k1=v1;k2=v2;k3=v3
cookieStr.split(';').forEach(item => {
if (!item) {
return
}
const arr = item.split('=')
const key = arr[0].trim()
const val = arr[1].trim()
req.cookie[key] = val
})
登录验证测试
手动修改前端cookie进行测试,之后会先登录再测试
在app.js中操作cookie(服务端)
- 登录成功则将信息存入cookie
- 登录后把信息放入cookies
res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
- cookies限制
限制前台修改cookies httpOnly
限制过期时间 expires=${getCookieExpires()
// 获取 cookie 的过期时间
const getCookieExpires = () => {
const d = new Date()
d.setTime(d.getTime() + (24 * 60 * 60 * 1000))
console.log('d.toGMTString() is ', d.toGMTString())
return d.toGMTString()
}
session
介绍
-
cookie会暴露username很危险
-
如何解决:cookie中存userid,server端对应username
-
解决方案:session,即server端存储用户信息
解析session
// // session 数据
const SESSION_DATA = {}
// 解析 session
let needSetCookie = false
let userId = req.cookie.userid
if (userId) {
if (!SESSION_DATA[userId]) {
SESSION_DATA[userId] = {}
}
} else {
needSetCookie = true
userId = `${Date.now()}_${Math.random()}`
SESSION_DATA[userId] = {}
}
req.session = SESSION_DATA[userId]
app.js转发
const userResult = handleUserRouter(req, res) //登陆路由
if (userResult) {
userResult.then(userData => {
if (needSetCookie) {
res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
}
res.end(
JSON.stringify(userData)
)
})
return
}
登录路由控制
if (method === 'POST' && req.path === '/api/user/login') {
const { username, password } = req.body
// const { username, password } = req.query
const result = login(username, password)
return result.then(data => {
if (data.username) {
// 设置 session
req.session.username = data.username
req.session.realname = data.realname
return new SuccessModel()
}
return new ErrorModel('登录失败')
})
}
登录验证
// 登录验证的测试
if (method === 'GET' && req.path === '/api/user/login-test') {
if (req.session.username) {
return Promise.resolve(
new SuccessModel({
session: req.session
})
)
}
return Promise.resolve(
new ErrorModel('尚未登录')
)
}
redis
session的问题
-
目前session是js 变量,放在nodejs进程内存中。
-
第一,进程内存有限,访问量过大,内存暴增怎么办?
-
第二,正式上线是多进程,进程之间无法共享。
解决方案redis
redis介绍
-
web sever最长用的缓存数据库,数据库存放在内存中
-
相比于MySQL,访问速度快(内存和硬盘不受一个数量级别的)
-
但是成本更高,可存储的数据更小(内存的硬伤)
方法
-
将web sever和redis拆分为两个单独的服务
-
双方都是独立的,都是可扩展的(例如都扩展成集群)
-
(包括MySQL,也是一个单独的服务,也可扩展)
为何session适合于redis
- session访问频繁,对性能要求极高
- session可不考虑断电丢失数据的问题(内存的硬伤)
- session数据量不会太大(相对于MySQL中存储的数据)
为何网站数据不适合redis
- 操作频率不是太高(相比于session操作)
- 断电不能丢失,必须保留
- 数据量太大,内存成本太高
安装
npm i redis --save
使用
const redis = require('redis')
// 创建客户端
const redisClient = redis.createClient(6379, '127.0.0.1')
redisClient.on('error', err => {
console.error(err)
})
// 测试
redisClient.set('myname', 'zhangsan2', redis.print)
redisClient.get('myname', (err, val) => {
if (err) {
console.error(err)
return
}
console.log('val ', val)
// 退出
redisClient.quit()
})
封装工具函数
const redis = require('redis')
const { REDIS_CONF } = require('../conf/db.js')
// 创建客户端
const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host)
redisClient.on('error', err => {
console.error(err)
})
function set(key, val) {
if (typeof val === 'object') {
val = JSON.stringify(val)
}
redisClient.set(key, val, redis.print)
}
function get(key) {
const promise = new Promise((resolve, reject) => {
redisClient.get(key, (err, val) => {
if (err) {
reject(err)
return
}
if (val == null) {
resolve(null)
return
}
try {
resolve(
JSON.parse(val)
)
} catch (ex) {
resolve(val)
}
})
})
return promise
}
module.exports = {
set,
get
}
解析session
// 解析 session (使用 redis)
let needSetCookie = false
let userId = req.cookie.userid
if (!userId) {
needSetCookie = true
userId = `${Date.now()}_${Math.random()}`
// 初始化 redis 中的 session 值
set(userId, {})
}
// 获取 session
req.sessionId = userId
get(req.sessionId).then(sessionData => {
if (sessionData == null) {
// 初始化 redis 中的 session 值
set(req.sessionId, {})
// 设置 session
req.session = {}
} else {
// 设置 session
req.session = sessionData
}
// console.log('req.session ', req.session)
// 处理 post data
return getPostData(req)
})
.then(postData => {
req.body = postData
//各种路由,路由转发后,根据需要写信息入cookies
.....
}
在登录路由中同步数据到redis
if (method === 'POST' && req.path === '/api/user/login') {
const { username, password } = req.body
// const { username, password } = req.query
const result = login(username, password)
return result.then(data => {
if (data.username) {
// 设置 session
req.session.username = data.username
req.session.realname = data.realname
// 同步到 redis
set(req.sessionId, req.session)
return new SuccessModel()
}
return new ErrorModel('登录失败')
})
}
bolg中的登录验证
// 统一的登录验证函数
const loginCheck = (req) => {
if (!req.session.username) {
return Promise.resolve(
new ErrorModel('尚未登录')
)
}
}
前后端联调
-
cookies跨域不共享,前端运行端口和后端运行端口不一致
-
前端页面安装http-server,启动在8001端口,后端在8000端口
-
用nginx统一转发至8080端口
nginx
配置 server监听8080端口