理解 cookie、session、token、jwt

发展史

  • 1、很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的 HTTP 协议, 就是请求加响应, 尤其是我不用记住是谁刚刚发了 HTTP 请求, 每个请求对我来说都是全新的。
  • 2、但是随着交互式 Web 应用的兴起,像在线购物网站,需要登录的网站等等,马上就面临一个问题,那就是要管理会话,必须记住哪些人登录系统, 哪些人往自己的购物车中放商品, 也就是说我必须把每个人区分开,这就是一个不小的挑战,因为HTTP请求是无状态的,所以想出的办法就是给大家发一个会话标识 session id, 说白了就是一个随机的字串,每个人收到的都不一样, 每次大家向我发起HTTP请求的时候,把这个字符串给一并捎过来, 这样我就能区分开谁是谁了
  • 3、这样大家很嗨皮了,可是服务器就不嗨皮了,每个人只需要保存自己的 session id,而服务器要保存所有人的 session id !如果访问服务器多了, 就得由成千上万,甚至几十万个。

    这对服务器说是一个巨大的开销 , 严重的限制了服务器扩展能力, 比如说我用两个机器组成了一个集群, 小F通过机器A登录了系统, 那session id会保存在机器A上, 假设小F的下一次请求被转发到机器B怎么办?机器B可没有小F的 session id啊。有时候会采用一点小伎俩:session sticky , 就是让小F的请求一直粘连在机器A上, 但是这也不管用, 要是机器A挂掉了, 还得转到机器B去。那只好做 session 的复制了, 把session id 在两个机器之间搬来搬去, 快累死了。

理解 cookie、session、token

后来有个叫 Memcached的支了招:把session id集中存储到一个地方, 所有的机器都来访问这个地方的数据, 这样一来,就不用复制了, 但是增加了单点失败的可能性, 要是那个负责session 的机器挂了, 所有人都得重新登录一遍, 估计得被人骂死。
理解 cookie、session、token
也尝试把这个单点的机器也搞出集群,增加可靠性, 但不管如何, 这小小的session 对我来说是一个沉重的负担

  • 4、于是有人就一直在思考, 我为什么要保存这可恶的session呢, 只让每个客户端去保存该多好?

可是如果不保存这些session id, 怎么验证客户端发给我的session id 的确是我生成的呢?** 如果不去验证,我们都不知道他们是不是合法登录的用户, 那些不怀好意的家伙们就可以伪造session id , 为所欲为了。

嗯,对了,关键点就是验证 !

比如说, 小F已经登录了系统, 我给他发一个令牌token, 里边包含了小F的 user id, 下一次小F 再次通过Http请求访问我的时候, 把这个token 通过Http header带过来不就可以了。*

不过这和session id没有本质区别啊, 任何人都可以可以伪造, 所以我得想点儿办法, 让别人伪造不了。

那就对数据做一个签名吧, 比如说我用HMAC-SHA256 算法,加上一个只有我才知道的密钥, 对数据做一个签名, 把这个签名和数据一起作为token, 由于密钥别人不知道, 就无法伪造token了。
理解 cookie、session、token

这个 token 我不保存, 当小F把这个 token 给我发过来的时候,我再用同样的 HMAC-SHA256 算法和同样的密钥,对数据再计算一次签名, 和 token 中的签名做个比较, 如果相同, 我就知道小F已经登录过了,并且可以直接取到小F的user id , 如果不相同, 数据部分肯定被人篡改过, 我就告诉发送者:对不起,没有认证。
理解 cookie、session、token

Token 中的数据是明文保存的(虽然我会用Base64做下编码, 但那不是加密), 还是可以被别人看到的, 所以我不能在其中保存像密码这样的敏感信息。

当然, 如果一个人的token 被别人偷走了, 那我也没办法, 我也会认为小偷就是合法用户, 这其实和一个人的session id 被别人偷走是一样的。

这样一来, 我就不保存session id 了, 我只是生成token , 然后验证token , 我用我的CPU计算时间获取了我的session 存储空间 !

解除了session id这个负担, 可以说是无事一身轻, 我的机器集群现在可以轻松地做水平扩展, 用户访问量增大, 直接加机器就行。这种无状态的感觉实在是太好了!

Cookie

  • cookie 是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。
  • cookie 由服务器生成,发送给浏览器,浏览器把 cookie 以 kv 形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该 cookie 发送给服务器。由于 cookie 是存在客户端上的,所以浏览器加入了一些限制确保 cookie 不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的 cookie 数量是有限的。

Session

  • session 从字面上讲,就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。
  • session 也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。
  • 服务器使用 session 把用户的信息临时保存在了服务器上,用户离开网站后 session 会被销毁。这种用户信息存储方式相对 cookie 来说更安全,可是 session 有一个缺陷:如果 web 服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候 session 会丢失。

Token

  • 在 Web 领域基于 Token 的身份验证随处可见。在大多数使用 Web API 的互联网公司中,tokens 是多用户下处理认证的最佳方式。
  • 以下几点特性会让你在程序中使用基于 Token 的身份验证
    1. 无状态、可扩展
    2. 支持移动设备
    3. 跨程序调用
    4. 安全
  • 那些使用基于 Token 的身份验证的大佬们
  • 大部分你见到过的 API 和 Web 应用都使用 tokens。例如 Facebook , Twitter , Google+ , GitHub 等。

起源

基于服务器的验证

  • 我们都是知道 HTTP 协议是无状态的,这种无状态意味着程序需要验证每一次请求,从而辨别客户端的身份。
  • 在这之前,程序都是通过在服务端存储的登录信息来辨别请求的。这种方式一般都是通过存储 Session 来完成。
  • 随着 Web ,应用程序,已经移动端的兴起,这种验证的方式逐渐暴露出了问题。尤其是在可扩展性方面。

基于服务器验证方式暴露的一些问题

  • Seesion:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加。
  • 可扩展性:在服务端的内存中使用Seesion存储登录信息,伴随而来的是可扩展性问题。
  • CORS (跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个让人头疼的问题。在使用 Ajax 抓取另一个域的资源,就可能会出现禁止请求的情况。
  • CSR F(跨站请求伪造):用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站。

在这些问题中,可扩展行是最突出的。因此我们有必要去寻求一种更有行之有效的方法。

基于Token的验证原理

基于Token的身份验证是无状态的,我们不将用户信息存在服务器或Session中。

这种概念解决了在服务端存储信息时的许多问题

NoSession意味着你的程序可以根据需要去增减机器,而不用去担心用户是否登录。

基于 Token 的身份验证的过程如下:

  1. 用户通过用户名和密码发送请求。
  2. 程序验证。
  3. 程序返回一个签名的token 给客户端。
  4. 客户端储存token,并且每次用于每次发送请求。
  5. 服务端验证token并返回数据。

每一次请求都需要 tokentoken 应该在HTTP的头部发送从而保证了Http请求无状态。我们同样通过设置服务器属性Access-Control-Allow-Origin:* ,让服务器能接受到来自所有域的请求。

需要注意的是,在 ACAO 头部标明 (designating)* 时,不得带有像 HTTP 认证,客户端 SSL 证书和 cookies 的证书

实现思路:

理解 cookie、session、token

  1. 用户登录校验,校验成功后就返回Token给客户端。
  2. 客户端收到数据后保存在客户端
  3. 客户端每次访问 API 是携带Token到服务器端。
  4. 服务器端采用 filter 过滤器校验。校验成功则返回请求数据,校验失败则返回错误码

当我们在程序中认证了信息并取得token之后,我们便能通过这个Token做许多的事情。

我们甚至能基于创建一个基于权限的token传给第三方应用程序,这些第三方程序能够获取到我们的数据(当然只有在我们允许的特定的token

Tokens的优势

无状态、可扩展

  • 在客户端存储的 Tokens 是无状态的,并且能够被扩展。基于这种无状态和不存储 Session 信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。
  • 如果我们将已验证的用户的信息保存在 Session 中,则每次请求都需要用户向已验证的服务器发送验证信息(称为 Session 亲和性)。用户量大时,可能会造成一些拥堵。
  • 但是不要着急。使用 tokens 之后这些问题都迎刃而解,因为 tokens 自己 hold 住了用户的验证信息。

安全性

  • 请求中发送 token 而不再是发送 cookie 能够防止 CSRF (跨站请求伪造)。即使在客户端使用 cookie 存储 tokencookie 也仅仅是一个存储机制而不是用于认证。不将信息存储在 Session 中,让我们少了对 session 操作。
  • token 是有时效的,一段时间之后用户需要重新验证。我们也不一定需要等到 token 自动失效,token 有撤回的操作,通过 token revocataion可以使一个特定的 token 或是一组有相同认证的 token 无效。

可扩展性

  • Tokens 能够创建与其它程序共享权限的程序。例如,能将一个随便的社交帐号和自己的大号 (Fackbook或是Twitter) 联系起来。当通过服务登录 Twitter (我们将这个过程Buffer)时,我们可以将这些 Buffer 附到 Twitter 的数据流上 (we are allowing Buffer to post to our Twitter stream)。
  • 使用tokens时,可以提供可选的权限给第三方应用程序。当用户想让另一个应用程序访问它们的数据,我们可以通过建立自己的 API,得出特殊权限的tokens

多平台跨域

我们提前先来谈论一下 CORS (跨域资源共享),对应用程序和服务进行扩展的时候,需要介入各种各种的设备和应用程序。

Having our API just serve data, we can also make the design choice to serve assets from a CDN. This eliminates the issues that CORS brings up after we set a quick header configuration for our application.

只要用户有一个通过了验证的token,数据和资源就能够在任何域上被请求到。

Access-Control-Allow-Origin: *      
  • 基于标准创建 token 的时候,你可以设定一些选项。标准的用法在JSON Web Tokens体现。
  • 最近的程序和文档是供给JSON Web Tokens的。它支持众多的语言。这意味在未来的使用中你可以真正的转换你的认证机制。

转自:微信公众号: 民工哥技术之路

JWT

是什么

  • JSON Web Token (JWT) 是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
  • 简单来说就是 JWT(Json Web Token)是实现token技术的一种解决方案

何时使用

  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JSON Web Token 结构

简述

  • JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
    • Header
    • Payload
    • Signature
  • 典型的JWT
    xxxxx.yyyyy.zzzzz

Header

header典型的由两部分组成:token的类型(“JWT”)和算法名称 alg(比如:HMAC SHA256或者RSA等等)。

理解 cookie、session、token、jwt

然后,用Base64对这个JSON编码就得到JWT的第一部分

Payload

JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。

  • Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
  • Public claims : 可以随意定义。
  • Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
    • iss 【issuer】发布者的url地址
    • sub 【subject】该JWT所面向的用户,用于处理特定应用,不是常用的字段
    • aud 【audience】接受者的url地址
    • exp 【expiration】 该jwt销毁的时间;unix时间戳
    • nbf 【not before】 该jwt的使用时间不能早于该时间;unix时间戳
    • iat 【issued at】 该jwt的发布时间;unix 时间戳
    • jti 【JWT ID】 该jwt的唯一ID编号

理解 cookie、session、token、jwt

对payload进行Base64编码就得到JWT的第二部分

注意:不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

Signature

为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。

例如:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

理解 cookie、session、token、jwt

如何工作

  • 在认证的时候,当用户用他们的凭证成功登录以后,一个JSON Web Token将会被返回。此后,token就是用户凭证了,你必须非常小心以防止出现安全问题。一般而言,你保存令牌的时候不应该超过你所需要它的时间。
  • 无论何时用户想要访问受保护的路由或者资源的时候,用户代理(通常是浏览器)都应该带上JWT,典型的,通常放在Authorization header中,用Bearer schema。
  • header应该看起来是这样的:
    • Authorization: Bearer <token>
  • 服务器上的受保护的路由将会检查Authorization header中的JWT是否有效,如果有效,则用户可以访问受保护的资源。如果JWT包含足够多的必需的数据,那么就可以减少对某些操作的数据库查询的需要,尽管可能并不总是如此。
  • 如果token是在授权头(Authorization header)中发送的,那么跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。
  • 下面这张图显示了如何获取JWT以及使用它来访问APIs或者资源:

理解 cookie、session、token、jwt

  1. 应用(或者客户端)想授权服务器请求授权。例如,如果用授权码流程的话,就是/oauth/authorize
  2. 当授权被许可以后,授权服务器返回一个access token给应用
  3. 应用使用access token访问受保护的资源(比如:API)

基于Token的身份认证 与 基于服务器的身份认证

基于服务器的身份认证

传统的做法是将已经认证过的用户信息存储在服务器上,比如Session。用户下次请求的时候带着Session ID,然后服务器以此检查用户是否认证过。

这种基于服务器的身份认证方式存在一些问题:

  • Sessions : 每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大。
  • Scalability : 由于Session是在内存中的,这就带来一些扩展性的问题。
  • CORS : 当我们想要扩展我们的应用,让我们的数据被多个移动设备使用时,我们必须考虑跨资源共享问题。当使用AJAX调用从另一个域名下获取资源时,我们可能会遇到禁止请求的问题。
  • CSRF : 用户很容易受到CSRF攻击。

JWT与Session的差异

  • 相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。
  • Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。
  • 而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
  • Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

理解 cookie、session、token、jwt

基于Token的身份认证是如何工作的

基于Token的身份认证是无状态的,服务器或者Session中不会存储任何用户信息。

没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。

虽然这一实现可能会有所不同,但其主要流程如下:

  1. 用户携带用户名和密码请求访问
  2. 服务器校验用户凭据
  3. 应用提供一个token给客户端
  4. 客户端存储token,并且在随后的每一次请求中都带着它
  5. 服务器校验token并返回数据

注意:

  1. 每一次请求都需要token
  2. Token应该放在请求header中
  3. 我们还需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin: *

JWT与OAuth的区别

  1. OAuth2是一种授权框架 ,JWT是一种认证协议
  2. 无论使用哪种方式切记用HTTPS来保证数据的安全性
  3. OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。

JWT 优点

  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展

JWT的使用

参考链接: 博客:JWT 完整使用详解

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 4年前 自动加精
讨论数量: 7

可以说很详细了,再提个jwt

4年前 评论

文章不错 收藏

4年前 评论

JWT相对于自定义的token机制有什么优势呢?

4年前 评论

我可能更喜欢用Token一点,Oauth2用到

4年前 评论

分析的不错,先收藏

3年前 评论

很棒的文章,先收藏了

3年前 评论

非常详细,就是有点重复的内容,学习到了 谢谢!

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!