session-cookie 和token登陸驗證

  最近研究了下基於token的身份驗證,並將這種機制整合在我的項目中。如今不少網站的認證方式都從傳統的seesion+cookie轉向token校驗。對比傳統的校驗方式,token確實有更好的擴展性與安全性。
  
  傳統的session+cookie身份驗證
  
  因爲HTTP是無狀態的,它並不記錄用戶的身份。用戶將帳號與密碼發送給服務器後,後臺經過校驗,可是並無記錄狀態,因而下一次用戶的請求仍然須要校驗身份。爲了解決這一問題,須要在服務端生成一條包含用戶身份的記錄,也就是session,再將這條記錄發送給用戶並存儲在用戶本地,即cookie。接下來用戶的請求都會帶上這條cookie,若客戶端的cookie與服務端的session能對應上,則說明用戶身份驗證經過。
  
  token身份校驗
  
  流程大體以下:
  
  第一次請求時,用戶發送帳號與密碼
  
  後臺校驗經過,則會生成一個有時效性的token,再將此token發送給用戶
  
  用戶得到token後,將此token存儲在本地,通常存儲在localstorage或cookie
  
  以後的每次請求都會將此token添加在請求頭裏,全部須要校驗身份的接口都會被校驗token,若token解析後的數據包含用戶身份信息,則身份驗證經過。
  
  對比傳統的校驗方式,token校驗有以下優點:
  
  在基於token的認證,token經過請求頭傳輸,而不是把認證信息存儲在session或者cookie中。這意味着無狀態。你能夠從任意一種能夠發送HTTP請求的終端向服務器發送請求。
  
  能夠避免CSRF攻擊
  
  當在應用中進行 session的讀,寫或者刪除操做時,會有一個文件操做發生在操做系統的temp 文件夾下,至少在第一次時。假設有多臺服務器而且 session 在第一臺服務上建立。當你再次發送請求而且這個請求落在另外一臺服務器上,session 信息並不存在而且會得到一個「未認證」的響應。我知道,你能夠經過一個粘性 session 解決這個問題。然而,在基於 token 的認證中,這個問題很天然就被解決了。沒有粘性 session 的問題,由於在每一個發送到服務器的請求中這個請求的 token 都會被攔截。
  
  下面介紹一下利用node+jwt(jwt教程)搭建簡易的token身份校驗
  
  示例
  
  當用戶第一次登陸時,提交帳號與密碼至服務器,服務器校驗經過,則生成對應的token,代碼以下:
  
  const fs = require('fs');
  
  const path = require('path');
  
  const jwt = require('jsonwebtoken');
  
  //生成token的方法
  
  function generateToken(data){
  
  let created = Math.floor(Date.now() / 1000);
  
  let cert = fs.readFileSync(path.join(__dirname, '../config/pri.pem'));//私鑰
  
  let token = jwt.sign({
  
  data,
  
  exp: created + 3600 * 24
  
  }, cert, {algorithm: 'RS256'});
  
  return token;
  
  }
  
  //登陸接口
  
  router.post('/oa/login', async (ctx, next) => {
  
  let data = ctx.request.body;
  
  let {name, password} = data;
  
  let sql = 'SELECT uid FROM t_user WHERE name=? and password=? and is_delete=0', value = [name, md5(password)];
  
  await db.query(sql, value).then(res => {
  
  if (res && res.length > 0) {
  
  let val = res[0];
  
  let uid = val['uid'];
  
  let token = generateToken({uid});
  
  ctx.body = {
  
  ...Tips[0], data: {token}
  
  }
  
  } else {
  
  ctx.body www.michenggw.com/= Tips[1006];
  
  }
  
  }).catch(e =www.yigouyule2.cn> {
  
  ctx.body = www.mhylpt.com/ Tips[1002];
  
  });
  
  });
  
  用戶經過校驗將獲取到的token存放在本地:
  
  ?
  
  1
  
  store.set('loginedtoken',token);//store爲插件
  
  以後客戶端請求須要驗證身份的接口,都會將token放在請求頭裏傳遞給服務端:node

  service.interceptors.request.use(config => {
  
  let params = config.params |www.gcyl158.com| {};
  
  let loginedtoken = store.get('loginedtoken');
  
  let time = Date.now(www.gcyL157.com);
  
  let {headers} = config;
  
  headers = {.www.feifanyule.cn/..headers,loginedtoken};
  
  params = {...params,_:time};
  
  config = {...config,params,headers};
  
  return config;
  
  }, error => {
  
  Promise.reject(error);
  
  })
  
  服務端對全部須要登陸的接口均攔截token並校驗合法性。
  
  ?
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  function verifyToken(token){
  
  let cert = fs.readFileSync(path.join(__dirname, '../config/pub.pem'));//公鑰
  
  try{
  
  let result = jwt.verify(token, cert, {algorithms: ['RS256']}) || {};
  
  let {exp = 0} = result,current = Math.floor(Date.now()/1000);
  
  if(current <= exp){
  
  res = result.data || {};
  
  }
  
  }catch(e){
  
  }
  
  return res;
  
  }
  
  app.use(async(ctx, next) => {
  
  let {url = ''} = ctx;
  
  if(url.indexOf('/user/') > -1){//須要校驗登陸態
  
  let header = ctx.request.header;
  
  let {loginedtoken} = header;
  
  if (loginedtoken) {
  
  let result = verifyToken(loginedtoken);
  
  let {uid} = result;
  
  if(uid){
  
  ctx.state = {uid};
  
  await next();
  
  }else{
  
  return ctx.body = Tips[1005];
  
  }
  
  } else {
  
  return ctx.body = Tips[1005];
  
  }
  
  }else{
  
  await next();
  
  }
  
  });
  
  本示例使用的公鑰與私鑰可本身生成,操做以下:
  
  打開命令行工具,輸入openssl,打開openssl;
  
  生成私鑰:genrsa -out rsa_private_key.pem 2048
  
  生成公鑰: rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pemweb

相關文章
相關標籤/搜索