1. 程式人生 > >session-cookie 和token登入驗證

session-cookie 和token登入驗證

最近研究了下基於token的身份驗證,並將這種機制整合在個人專案中。現在很多網站的認證方式都從傳統的seesion+cookie轉向token校驗。對比傳統的校驗方式,token確實有更好的擴充套件性與安全性。

傳統的session+cookie身份驗證

由於HTTP是無狀態的,它並不記錄使用者的身份。使用者將賬號與密碼傳送給伺服器後,後臺通過校驗,但是並沒有記錄狀態,於是下一次使用者的請求仍然需要校驗身份。為了解決這一問題,需要在服務端生成一條包含使用者身份的記錄,也就是session,再將這條記錄傳送給使用者並存儲在使用者本地,即cookie。接下來使用者的請求都會帶上這條cookie,若客戶端的cookie與服務端的session能對應上,則說明使用者身份驗證通過。

token身份校驗

流程大致如下:

  1. 第一次請求時,使用者傳送賬號與密碼
  2. 後臺校驗通過,則會生成一個有時效性的token,再將此token傳送給使用者
  3. 使用者獲得token後,將此token儲存在本地,一般儲存在localstorage或cookie
  4. 之後的每次請求都會將此token新增在請求頭裡,所有需要校驗身份的介面都會被校驗token,若token解析後的資料包含使用者身份資訊,則身份驗證通過。

對比傳統的校驗方式,token校驗有如下優勢:

  1. 在基於token的認證,token通過請求頭傳輸,而不是把認證資訊儲存在session或者cookie中。這意味著無狀態。你可以從任意一種可以傳送HTTP請求的終端向伺服器傳送請求。
  2. 可以避免CSRF攻擊
  3. 當在應用中進行 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 = Tips[1006];

    }

  }).catch(e => {

    ctx.body = Tips[1002];

  });

 

});

使用者通過校驗將獲取到的token存放在本地:

?

1

store.set('loginedtoken',token);//store為外掛

之後客戶端請求需要驗證身份的介面,都會將token放在請求頭裡傳遞給服務端:

?

1

2

3

4

5

6

7

8

9

10

11

12

service.interceptors.request.use(config => {

  let params = config.params || {};

  let loginedtoken = store.get('loginedtoken');

  let time = Date.now();

  let {headers} = config;

  headers = {...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();

  }

});

本示例使用的公鑰與私鑰可自己生成,操作如下:

  1. 開啟命令列工具,輸入openssl,開啟openssl;
  2. 生成私鑰:genrsa -out rsa_private_key.pem 2048
  3. 生成公鑰: rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

點此檢視node後臺程式碼 
點此檢視前端程式碼

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援指令碼之家。

 

文章出處:https://www.jb51.net/article/137981.htm