深刻理解令牌认证机制(token)

之前的开发模式是以MVC为主,可是随着互联网行业快速的发展逐渐的演变成了先后端分离,若项目中须要作登陆的话,那么token成为先后端惟一的一个凭证。前端

token即标志、记号的意思,在IT领域也叫做令牌。在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。通常做为邀请、登陆系统使用。java

token其实说的更通俗点能够叫暗号,在一些数据传输以前,要先进行暗号的核对,不一样的暗号被受权不一样的数据操做。例如在USB1.1协议中定义了4类数据包:token包、data包、handshake包和special包。主机和USB设备之间连续数据的交换能够分为三个阶段,第一个阶段由主机发送token包,不一样的token包内容不同(暗号不同)能够告诉设备作不一样的工做,第二个阶段发送data包,第三个阶段由设备返回一个handshake包。ios

在HTTP请求中使用承载令牌来访问OAuth 2.0受保护的资源。拥有承载令牌的任何一方(“承载方”)均可以使用它访问相关资源(无需证实拥有加密密钥)。为了防止误用,须要防止在存储和传输中泄露承载令牌。web

OAuth容许客户端经过获取访问令牌,它在“OAuth 2.0受权”中定义框架“[RFC6749]做为”表示访问的字符串而不是使用资源直接服务的凭证。算法

该令牌由服务端容许的状况下,由客户端经过某种方式向服务端发出请求,由服务端向客户端发出,客户机使用访问令牌访问由资源服务器承载的受保护的资源。该规范描述了当OAuth访问令牌是承载令牌时,如何发出受保护的资源请求。json

客户端只须要拥有token能够以任何一种方法传递token,客户端须要知道参数加密的密钥,只须要存储token便可。axios

OAuth为客户端提供了一种方法来表明资源全部者访问受保护的资源。在通常状况下,客户机在访问受保护的资源以前,必须首先从资源全部者得到受权,而后将受权交换为访问令牌。访问令牌表示受权授予授予的范围、持续时间和其余属性。客户机经过向资源服务器显示访问令牌来访问受保护的资源。在某些状况下,客户端能够直接向服务端显示的发送本身的凭证。后端

+--------+                               +---------------+
 |        |--(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]开发的通用身份验证框架兼容,尽管它没有遵循其中列出的反映现有部署的首选实践。承载凭证的语法以下api

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

客户端应该使用带有承载HTTP受权方案的Authorization请求头字段使用承载令牌发出通过身份验证的请求。资源服务器必须支持此方法服务器

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加密的,有些内容是能够反解的,因此千万不要在数据里面添加有关数据的敏感信息。注意注意。。。

相关文章
相关标签/搜索