Appearance
cookie
跨域请求中服务器给浏览器设置 Cookie 需要同时满足下面三个条件:
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080') 没有设置为 *
res.setHeader('Access-Control-Allow-Credentials', 'true')
客户端开启 withCredentials
javascript
const http = require('http')
const uuid = require('uuid')
const crypto = require('crypto')
// 一般通过 OpenSSL 生成 1024 字节的密钥
const key = '0.27276630965318804'
const session = Object.create(null)
// 签名
const sign = v => {
return crypto.createHmac('sha256', key).update(v.toString()).digest('base64')
}
const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5501')
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, POST')
res.setHeader('Access-Control-Allow-Credentials', 'true')
req.getCookie = (key, options = {}) => {
const cookies = req.headers.cookie.split('; ')
const cookieMap = Object.create(null)
for (let i = 0, l = cookies.length; i < l; i++) {
const cookie = cookies[i]
const index = cookie.indexOf('=')
const key = cookie.slice(0, index)
const value = cookie.slice(index + 1)
cookieMap[key] = value
}
const val = cookieMap[key]
if (!val) {
return ''
}
if (options.signed) {
const [rawVal, signed] = val.split('.')
if (sign(rawVal) === signed) {
return signed
}
return '' // cookie 被篡改
}
return val
}
res.setCookie = (() => {
// 一次设置多个 cookie
let cookie = ''
return (key, val, options = {}) => {
// 签名,同时还要保留原来的值,用于后续验证 cookie 是否被篡改,因此 cookie 不存
// 敏感信息。
// 不用 MD5,可以根据值计算出 MD5,然后修改为计算出的 MD5。
if (options.sign) {
val += `.${ sign(val) }`
delete options.sign
}
cookie += `${ key }=${ val }; `
for (const option in options) {
cookie += `${ option }=${ options[option] }; `
}
res.setHeader('Set-Cookie', cookie)
}
})()
if (req.url === '/foo') {
res.setCookie('foo', 'foo', { 'max-age': 1000000, sign: true })
// res.setCookie('bar', 'bar', { 'max-age': 1000000})
res.end()
}
if (req.url === '/bar') {
res.end(req.getCookie('foo', { signed: true }))
}
})
server.listen(3000)session
javascript
if (req.url === '/baz') {
const cookie = req.getCookie('connect.sid')
// 之后登录
if (cookie && session[cookie]) {
res.end()
}
// 第一次登录或 cookie 过期或服务器重启等
else {
const _uuid = uuid.v4()
// 如果不想在 cookie 中暴露任何信息,使用 session,用户信息可以保存在服务端,
// 但比较少这样做,一般 session 中都是保存着用户标识,用这个标识去数据库中
// 取数据。
session[_uuid] = {}
res.setCookie('connect.sid', _uuid)
res.end()
}
}流程
表单校验防止 SQL 注入和 XSS 攻击。
对于隐私信息进行 MD5 加密。
向服务器发送 POST 请求。
服务器获取传递的数据。
去数据库中查找符合账号和密码的用户
没找到,返回登录失败并提示;找到了,把用户的基本信息存储在服务器的 session 中并产生 一个唯一标识 connect.sid,在返回客户端信息时基于在响应头中的 Set-Cookie 字段中存储 connect.sid。
浏览器把 connect.sid 设置在本地的 cookie 中,之后进行登陆成功的提示和页面的跳转,还 可以用变量记录是否已经登录。
刷新或重新打开页面时,登录标识消失,向服务器发 GET 请求并携带 cookie 验证之前是否登 录过。
服务器获取到 connect.sid,去 session 中找,如果找到了,认为当前用户是登录的,否则 说明第一次登录或 cookie 过期 或服务器重启(如果 session 保存在服务器内存中)或 数据库中 session 丢失(session 持久化),重新生成 connect.sid,保存进 session 和 Set-Cookie 字段中。