深刻理解令牌認證機制(token)

之前的開發模式是以MVC爲主,可是隨着互聯網行業快速的發展逐漸的演變成了先後端分離,若項目中須要作登陸的話,那麼token成爲先後端惟一的一個憑證。javascript

token即標誌、記號的意思,在IT領域也叫做令牌。在計算機身份認證中是令牌(臨時)的意思,在詞法分析中是標記的意思。通常做爲邀請、登陸系統使用。前端

token其實說的更通俗點能夠叫暗號,在一些數據傳輸以前,要先進行暗號的核對,不一樣的暗號被受權不一樣的數據操做。例如在USB1.1協議中定義了4類數據包:token包、data包、handshake包和special包。主機和USB設備之間連續數據的交換能夠分爲三個階段,第一個階段由主機發送token包,不一樣的token包內容不同(暗號不同)能夠告訴設備作不一樣的工做,第二個階段發送data包,第三個階段由設備返回一個handshake包。java

在HTTP請求中使用承載令牌來訪問OAuth 2.0受保護的資源。擁有承載令牌的任何一方(「承載方」)均可以使用它訪問相關資源(無需證實擁有加密密鑰)。爲了防止誤用,須要防止在存儲和傳輸中泄露承載令牌。ios

OAuth容許客戶端經過獲取訪問令牌,它在「OAuth 2.0受權」中定義框架「[RFC6749]做爲」表示訪問的字符串而不是使用資源直接服務的憑證。web

該令牌由服務端容許的狀況下,由客戶端經過某種方式向服務端發出請求,由服務端向客戶端發出,客戶機使用訪問令牌訪問由資源服務器承載的受保護的資源。該規範描述了當OAuth訪問令牌是承載令牌時,如何發出受保護的資源請求。算法

客戶端只須要擁有token能夠以任何一種方法傳遞token,客戶端須要知道參數加密的密鑰,只須要存儲token便可。json

OAuth爲客戶端提供了一種方法來表明資源全部者訪問受保護的資源。在通常狀況下,客戶機在訪問受保護的資源以前,必須首先從資源全部者得到受權,而後將受權交換爲訪問令牌。訪問令牌表示受權授予授予的範圍、持續時間和其餘屬性。客戶機經過向資源服務器顯示訪問令牌來訪問受保護的資源。在某些狀況下,客戶端能夠直接向服務端顯示的發送本身的憑證。axios

+--------+                               +---------------+
 |        |--(A)- Authorization Request ->|   Resource    |
 |        |                               |     Owner     |
 |        |<-(B)-- Authorization Grant ---|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(C)-- Authorization Grant -->| Authorization |
 | Client |                               |     Server    |
 |        |<-(D)----- Access Token -------|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(E)----- Access Token ------>|    Resource   |
 |        |                               |     Server    |
 |        |<-(F)--- Protected Resource ---|               |
 +--------+                               +---------------+

此方案的Authorization頭字段的語法遵循[RFC2617]第2節中定義的基本方案的用法。注意,與Basic同樣,它不符合[RFC2617]第1.2節中定義的通用語法,但與正在爲HTTP 1.1 [HTTP- auth]開發的通用身份驗證框架兼容,儘管它沒有遵循其中列出的反映現有部署的首選實踐。承載憑證的語法以下後端

b64toke = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
credentials = "Bearer" 1*SP b64token

客戶端應該使用帶有承載HTTP受權方案的Authorization請求頭字段使用承載令牌發出通過身份驗證的請求。資源服務器必須支持此方法api

Internet Engineering Task Force (IETF)的白皮書中介紹Bearer Token的使用方法,那麼咱們平時使用token的時候姿式是否正確。應該如何正確使用token呢?

import axios from "axios";
axios.interceptors.request.use(config => {
  if (store.state.token) {
    config.headers.authorization = `Basic ${store.state.token}`;
  }
  return config;
});

照白皮書所說這樣纔是正確使用token的姿式。小夥伴們平時大家使用token的時候是這樣的嗎?

那麼除了前端有明確的使用規範,那麼服務端又應該怎樣有效的作好後端數據防禦?在白皮書中一樣也有提到過。

根據OAuth 2.0動態客戶端註冊協議該規範定義了向受權服務器動態註冊OAuth 2.0客戶端的機制。註冊請求向受權服務器發送一組所需的客戶端元數據值(token)。結果的註冊響應返回要在受權服務器上使用的客戶機標識符和爲客戶機註冊的客戶機元數據值。而後,客戶機可使用此註冊信息使用OAuth 2.0協議與受權服務器通訊。該規範還定義了一組通用客戶端元數據字段和值,供客戶端在註冊期間使用。

爲了讓OAuth 2.0 [RFC6749]客戶機利用OAuth 2.0受權服務器,客戶機須要與服務器交互的特定信息,包括在該服務器上使用的OAuth 2.0客戶端標識符。該規範描述瞭如何經過受權服務器動態註冊OAuth 2.0客戶端來獲取此信息。

抽象的動態客戶端註冊流程

+--------(A)- Initial Access Token (OPTIONAL)
    |
    |   +----(B)- Software Statement (OPTIONAL)
    |   |
    v   v
+-----------+                                      +---------------+
|           |--(C)- Client Registration Request -->|    Client     |
| Client or |                                      | Registration  |
| Developer |<-(D)- Client Information Response ---|   Endpoint    |
|           |        or Client Error Response      +---------------+
+-----------+

圖中所示的抽象OAuth 2.0客戶機動態註冊流描述了客戶機或開發人員與此規範中定義的端點之間的交互。此圖沒有顯示錯誤條件。這個流程包括如下步驟

  1. 可選地,向客戶端或開發人員發出初始訪問令牌,容許訪問客戶端註冊端點。向客戶端或開發人員發出初始訪問令牌的方法超出了本規範的範圍。
  2. 客戶端或開發人員能夠選擇發佈一個軟件聲明,以便與客戶端註冊端點一塊兒使用。向客戶端或開發人員發出軟件聲明的方法超出了本規範的範圍。
  3. 客戶端或開發人員使用客戶端所需的註冊元數據調用客戶端註冊端點,若是受權服務器須要初始訪問令牌,則能夠選擇包含來自(A)的初始訪問令牌。
  4. 受權服務器註冊客戶機並返回客戶端註冊的元數據, 在服務器上惟一的客戶端標識符,以及一組客戶端憑據,如客戶端機密(若是適用於此客戶端)。

受權類型與響應類型之間的關係

描述的「grant類型」和「響應類型」值是部分正交的,由於它們引用傳遞到OAuth協議中不一樣端點的參數。可是,它們是相關的,由於客戶機可用的grant類型影響客戶機可使用的響應類型,反之亦然。例如,包含受權代碼受權類型值意味着包含代碼響應類型值,由於這兩個值都定義爲OAuth 2.0受權代碼受權的一部分。所以,支持這些字段的服務器應該採起步驟,以確保客戶機不能將本身註冊到不一致的狀態,例如,經過向不一致的註冊請求返回無效的客戶機元數據錯誤響應。

下表列出了這兩個字段之間的相關性。

+-----------------------------------------------+-------------------+
| grant_types value includes:                   | response_types    |
|                                               | value includes:   |
+-----------------------------------------------+-------------------+
| authorization_code                            | code              |
| implicit                                      | token             |
| password                                      | (none)            |
| client_credentials                            | (none)            |
| refresh_token                                 | (none)            |
| urn:ietf:params:oauth:grant-type:jwt-bearer   | (none)            |
| urn:ietf:params:oauth:grant-type:saml2-bearer | (none)            |
+-----------------------------------------------+-------------------+

授予類型響應類型參數引入新值的此文檔的擴展和概要文件必須記錄這兩種參數類型之間的全部通訊。

若是發送任何人類可讀的字段時沒有使用語言標記,那麼使用該字段的各方不能對字符串值的語言、字符集或腳本作出任何假設,並且字符串值必須按照在用戶界面中顯示的位置使用。爲了促進互操做性,建議客戶端和服務器除了使用任何特定於語言的字段外,還使用不使用任何語言標記的人可讀字段,而且建議發送的任何不使用語言標記的人可讀字段包含適合在各類系統上顯示的值。

例如,軟件聲明能夠包含如下聲明:

{
  "software_id": "4NRB1-0XZABZI9E6-5SM3R",
  "client_name": "Example Statement-based Client",
  "client_uri": "https://client.example.net/"
}

如下非標準示例JWT包括這些聲明,而且使用RS256(僅用於顯示目的)進行了非對稱簽名。並等到以下加密字符串。

eyJhbGciOiJSUzI1NiJ9.
eyJzb2Z0d2FyZV9pZCI6IjROUkIxLTBYWkFCWkk5RTYtNVNNM1IiLCJjbGll
bnRfbmFtZSI6IkV4YW1wbGUgU3RhdGVtZW50LWJhc2VkIENsaWVudCIsImNs
aWVudF91cmkiOiJodHRwczovL2NsaWVudC5leGFtcGxlLm5ldC8ifQ.
GHfL4QNIrQwL18BSRdE595T9jbzqa06R9BT8w409x9oIcKaZo_mt15riEXHa
zdISUvDIZhtiyNrSHQ8K4TvqWxH6uJgcmoodZdPwmWRIEYbQDLqPNxREtYn0
5X3AR7ia4FRjQ2ojZjk5fJqJdQ-JcfxyhK-P8BAWBd6I2LLA77IG32xtbhxY
fHX7VhuU5ProJO8uvu3Ayv4XRhLZJY4yKfmyjiiKiPNe-Ia4SMy_d_QSWxsk
U5XIQl5Sa2YRPMbDRXttm2TfnZM1xx70DoYi8g6czz-CPGRi4SW_S2RKHIJf
IjoI3zTJ0Y2oe0_EJAiXbL6OyF9S5tKxDXV8JIndSA

加密字符串由頭,載荷以及密鑰經過一系列的速算法生成,加密字符串與頭,載荷以及密鑰息息相關,一但加密字符串稍有改動,則沒法解析正確解析沒法經過驗證。

經過加密字符串向受權服務器註冊客戶端。受權服務器爲該客戶端分配一個唯一的客戶端標識符,可選地分配一個客戶端機密,並將請求中提供的元數據與已發佈的客戶端標識符關聯起來。該請求包括在註冊期間爲客戶端指定的任何客戶端元數據參數。受權服務器能夠爲客戶端元數據中遺漏的任何項提供默認值。

註冊端點,內容類型爲application/json。該HTTP Entity Payload是一個由JSON組成的JSON文檔對象和全部請求的客戶端元數據值做爲頂級成員那個JSON對象。

示例:

const Koa = require("koa");
const Router = require("koa-router");
const jwt = require("jsonwebtoken");
const jwtAuth = require("koa-jwt");

const secret = "it's a secret";     //  密鑰
const app = new Koa();
const router = new Router();

router.get('/api/login',async (ctx) => {
    const {username,passwd} = ctx.query;
    if(username === "aaron" && passwd == "123456"){
        const token = jwt.sign({
            data:{name:"Aaron",userId:"1"},         //  用戶信息
            exp:Math.floor(Date.now()/1000)+60*60   //  過時時間
        },secret);
        ctx.body = {code:200,token};
    }
    else{
        ctx.status = 401;
        ctx.body = {code:0,message: "用戶名密碼錯誤"};
    }
});

router.get("/api/userinfo",jwtAuth({secret}),async (ctx) => {   //  jwtAuth受保護路由
    ctx.body = {code:200,data:{name:"Aaron",age:18}}
});

app.use(router.routes());
app.listen(3000);

由於最後生成的token是經過base64加密的,有些內容是能夠反解的,因此千萬不要在數據裏面添加有關數據的敏感信息。注意注意。。。

相關文章
相關標籤/搜索