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"關鍵詞進行定義