koa實戰 - 鑑權

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`)
})

實現原理:

  1. 服務器在接受客戶端首次訪問時在服務器端創建seesion,然後保存seesion(我們可以將
    seesion保存在內存中,也可以保存在redis中,推薦使用後者),然後給這個session生成一
    個唯一的標識字符串,然後在響應頭中種下這個唯一標識字符串。
  2. 簽名。這一步通過祕鑰對sid進行簽名處理,避免客戶端修改sid。
  3. 瀏覽器中收到請求響應的時候會解析響應頭,然後將sid保存在本地cookie中,瀏覽器在下次
    http請求的請求頭中會帶上該域名下的cookie信息,
  4. 服務器在接受客戶端請求時會去解析請求頭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驗證

原理

  1. 客戶端使用用戶名跟密碼請求登錄
  2. 服務端收到請求,去驗證用戶名與密碼
  3. 驗證成功後,服務端會簽發一個令牌(Token),再把這個 Token 發送給客戶端
  4. 客戶端收到 Token 以後可以把它存儲起來,比如放在 Cookie 裏或者 Local Storage 裏
  5. 客戶端每次向服務端請求資源的時候需要帶着服務端簽發的 Token
  6. 服務端收到請求,然後去驗證客戶端請求裏面帶着的 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、哈希

  1. 簽名:默認使用base64對payload編碼,使用hs256算法對令牌頭、payload和密鑰進行簽名生成哈希
  2. 驗證:默認使用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"關鍵詞進行定義

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章