session-cookie方式
const http = require('http')
const app = http.createServer((req, res) => {
console.log(`cookie: ${req.headers.cookie}`)
res.setHeader('Set-Cookie', 'cookie=admin');
res.end('set cookie')
})
app.listen(3000, () => {
console.log(`start server at localhost:3000`)
})
Set-Cookie
负责设置cookie;
请求传递cookie
session原理
const http = require('http')
const session = {}
http.createServer((req,res)=>{
const sessionKey = 'sid'
const cookie = req.headers.cookie;
console.log(cookie)
if(cookie && cookie.indexOf(sessionKey)>-1){
res.end('welcome back')
const pattern = new RegExp(`${sessionKey}=([^;]+);?\s*`)
const sid = pattern.exec(cookie)[1]
console.log(`session: ${sid}`,session,session[sid])
}else{
const sid = (Math.random()*99999999).toFixed()
res.setHeader('Set-Cookie',`${sessionKey}=${sid}`)
session[sid] = {name:'lau'}
res.end('welcome')
}
}).listen(3000,()=>{
console.log(`start server at localhost:3000`)
})
实现原理:
- 服务器在接受客户端首次访问时在服务器端创建seesion,然后保存seesion(我们可以将
seesion保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一
个唯一的标识字符串,然后在响应头中种下这个唯一标识字符串。 - 签名。这一步通过秘钥对sid进行签名处理,避免客户端修改sid。
- 浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次
http请求的请求头中会带上该域名下的cookie信息, - 服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端
保存的该客户端的session,然后判断该请求是否合法。
在koa中使用session
安装koa-session
npm i -S koa-session
const Koa = require('koa')
const app = new Koa();
const session = require('koa-session')
app.keys = ['secret']; // 签名key keys作用 用来对cookie进行签名
const SESS_CONFIG = { // 配置
key: 'koa:Session', //cookie key (default is koa:sess)
maxAge: 1000, // cookie的过期时间 maxAge in ms (default is 1 days)
httpOnly: true, //cookie是否只有服务器端可以访问 httpOnly or not (default true)
signed: true //签名默认true
}
app.use(session(SESS_CONFIG, app)) // 注册
app.use(ctx => {
let n = ctx.session.count || 0;
ctx.session.count = ++n;
ctx.body = `第${n}次访问了`
})
app.listen(3000, () => {
console.log(`start server at localhost:3000`)
})
使用redis存储session
要使用redis,先在官网下载后进行连接。
开启redis
node连接redis
const redis = require('redis')
const client = redis.createClient(6379,'127.0.0.1')
client.on('error',(err)=>{
console.log(`Error occur ${err}`)
})
client.set('name','leo',redis.print) // print 打印设置数据的结果
client.get('name',(err,val)=>{
if(err) throw err;
console.log(`Name : ${val}`)
})
在koa中使用redis
安装koa-redis
npm i -S koa-redis
const Koa = require('koa')
const app = new Koa();
const session = require('koa-session');
const koaRedis = require('koa-redis')
const redis = require('redis')
const redisClient = redis.createClient(6379,'127.0.0.1')
const wrapper = require('co-redis')
const client = wrapper(redisClient);
app.use(session({
key:'koa:Session',
store: koaRedis({client}) // 此处可以不必指定client
},app));
app.use(async (ctx,next)=>{
const keys = await client.keys('*')
keys.forEach(async key=>{
console.log(await client.get(key))
})
await next()
})
app.listen(3000, () => {
console.log(`start server at localhost:3000`)
})
token验证
原理
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个令牌(Token),再把这个 Token 发送给客户端
- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据。
const Koa = require('koa')
const router = require('koa-router')()
const jwt = require('jsonwebtoken');
const jwtAuth = require('koa-jwt');
const secret = 'secret key';
const app = new Koa();
router.get('/login', async (ctx) => {
console.log('access')
ctx.body = {
message: 'login success',
user: 'leo',
token: jwt.sign({
data: 'leo',
exp: Math.floor(Date.now() / 1000) + 5 // 5秒后过期
}, secret)
}
})
router.get('/token', jwtAuth({ secret }), async ctx => {
console.log(ctx.state.user);
ctx.body = {
message: 'get message',
user: ctx.state.user.data
}
})
app.use(async (ctx, next) => {
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (ctx.method == 'OPTIONS') {
ctx.body = 200;
} else {
await next();
}
})
app.use(router.routes())
app.listen(3000, () => {
console.log(`start server at localhost:3000`)
})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>token</title>
</head>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js"></script>
<body>
<div id="login">login</div>
<div id="getToken">getToken</div>
</body>
<script>
axios.interceptors.request.use( config => {
const token = window.localStorage.getItem("token");
if (token) { // 判断是否存在token,如果存在的话,则每个http header都加上token // Bearer是JWT的认证头部信息
config.headers.common["Authorization"] = "Bearer " + token;
}
return config;
},err => { return Promise.reject(err); } );
async function getResult(){
const res = await axios.get('http://localhost:3000/token');
console.log(res)
}
async function Login(){
const res = await axios.get('http://localhost:3000/login');
localStorage.setItem('token',res.data.token)
}
var login = document.querySelector('#login')
login.addEventListener('click',(e)=>{
Login()
})
var to = document.querySelector('#getToken')
to.addEventListener('click',(e)=>{
getResult()
})
</script>
</html>
如果token过期,则会返回401错误
jwt验证过程:
- 用户登录的时候,服务端生成一个token返回给客户端
- 客户端后续的请求都带上这个token
- 服务端解析token获取用户信息,并响应用户的请求
- token会有过期时间,客户端登出的时候也会废弃token,但是服务端不需要任何操作
JWT(JSON WEB TOKEN)原理
Bearer Token包含三个组成部分:令牌头、payload、哈希
- 签名:默认使用base64对payload编码,使用hs256算法对令牌头、payload和密钥进行签名生成哈希
- 验证:默认使用hs256算法对hs256算法对令牌中数据签名并将结果和令牌中哈希比对
const jwt = require('jsonwebtoken');
const secret = 'secret key'
const userinfo={
name:'leo',
password:'123'
}
const token = jwt.sign({
data:userinfo,
exp:Math.floor(Date.now()/1000) + 2 // 2s后过期
},secret)
console.log(token)
const options={
key:'userinfo'
}
console.log(`解码:`,jwt.verify(token,secret,options))
HMAC SHA256
HMAC(Hash Message Authentication Code,散列消息鉴别码,基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。
BASE64
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节
描述为一种不易被人直接识别的形式。常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64编码的
Beare
Beare作为一种认证类型(基于OAuth 2.0),使用"Bearer"关键词进行定义