详解JWT和Session,SAML, OAuth和SSO,

前言

了解什么是 OAuth,什么是 SSOSSO 下不一样策略 OAuthSAML 的不一样,以及 OAuthOpenID 的不一样,更重要的是区分 authorisation和 authentication
java

最后咱们引出 JSON WEB TOKEN,聊聊 JWT 在 Session 管理方面的优点和劣势,同时尝试解决这些劣势,看当作本和代价有多少。python

正文

本文关于 OAuth 受权和 API 调用实例都来自 GoogleAPI算法

关于Token

Token 即便是在计算机领域中也有不一样的定义,这里咱们说的 token,是指 访问资源 的凭据。例如当你调用 GoogleAPI 时,须要带上有效 token 来代表你请求的 合法性。这个 TokenGoogle 给你的,这表明 Google 给你的 受权 使得你有能力访问 API 背后的 资源json

请求 API 时携带 token 的方式也有不少种,经过 HTTPHeader 或者 url 参数或者 google 提供的类库均可以:后端

  • HTTP Headerapi


  
    
  
  
  
   
   
            
   
   
  1. 浏览器

  2. bash

  3. 服务器

GET /drive/v2/files HTTP/1.1Authorization: Bearer <token>Host: www.googleapis.com/


  • URL参数微信


  
    
  
  
  
   
   
            
   
   
GET https://www.googleapis.com/drive/v2/files?token=<token>


  • Python函数库


  
    
  
  
  
   
   
            
   
   
from googleapiclient.discovery import builddrive = build('drive', 'v2', credentials=credentials)

更具体的说,上面用于调用 APItoken,咱们称为细分为 access token。一般 access token 是有 有效期限 的,若是 过时 就须要 从新获取。那么如何从新获取?先看看第一次获取 token 的流程是怎样的:

  1. 首先须要向 GoogleAPI 注册一个应用程序,注册完毕以后就会拿到 认证信息credentials)包括 IDsecret。不是全部的程序类型都有 secret

  2. 接下来就要向 Google 请求 access token。这里先忽略一些细节,例如请求参数(固然须要上面申请到的 secret)。重要的是,若是你想访问的是 用户资源,这里就会提醒用户进行 受权

  3. 若是 用户受权 完毕。 Google 就会返回 access token。又或者是返回 受权代码authorization code),再经过代码取得 access token

  4. token 获取到以后,就可以带上 token 访问 API了。

流程以下图所示:

注意:在第三步经过 authorization code 兑换 access token 的过程当中, Google 并不会仅仅返回 access token,还会返回额外的信息,这其中和以后更新相关的就是 refresh token。一旦 access token 过时,你就能够经过 refresh token 再次请求 access token

以上只是大体的流程,而且故意省略了一些额外的概念。好比更新 access token 固然也能够不须要 refresh token,这要根据你的 请求方式 和访问的 资源类型 而定。

这里又会引发另外的两个问题:

  1. 若是 refesh token 也过时了怎么办?这时就须要用户 从新登录受权

  2. 为何要区分 refresh tokenaccess token?若是合并成一个 token而后把 过时时间 调整的 更长,而且每次 失效 以后用户 从新登录受权 就行了?这个问题会和后面谈的相关概念有关,后面会给予解释说明。

OAuth

从获取 token 到使用 token 访问接口。这实际上是标准的 OAuth2.0 机制下访问 API 的流程。这里介绍一下 OAuth 里外相关的概念,更深刻的理解 token的做用。

SSO (Single sign-on)

一般公司内部会有很是多的平台供你们使用,好比人力资源,代码管理,日志监控,预算申请等等。若是每个平台都实现本身的用户体系的话无疑是巨大的浪费,因此公司内部会有一套 公用的用户体系,用户只要登录以后,就可以 访问全部的系统。这就是 单点登陆

SSO 是一类 解决方案 的统称,而在具体的实施方面,咱们有两种策略可供选择:

  • SAML 2.0

  • OAuth 2.0

接下来咱们区别这 两种受权方式 有什么不一样。可是在描述 不一样的策略 以前,咱们先叙述几个 共有的特性,而且至关重要的概念。

Authentication VS Authorisation

  1. Authentication: 身份鉴别,如下简称 认证

  2. Authorisation: 资源访问 受权

认证 的做用在于 承认 你可以访问系统,用于 鉴别访问者 是不是 合法用户;而 受权 用于决定你有访问 哪些资源的权限

大多数人不会区分这二者的区别,由于站在用户的立场上。而做为系统的设计者来讲,这二者是有差异的,这是不一样的两个工做职责。咱们能够只须要 认证功能,而不须要 受权功能,甚至不须要本身实现 认证功能。而借助 Google 的认证系统,即用户能够用 Google 的帐号进行登录。

Authorization Server/Identity Provider(IdP)

把负责 认证的服务 称为 AuthorizationServer 或者 IdentityProvider,如下简称 IdP

Service Provider(SP)/Resource Server

把负责 提供资源API 调用)的服务称为 ResourceServer 或者 ServiceProvider,如下简称 SP

SAML 2.0

下图是 SAML2.0 的流程图,看图说话:

  1. 未登录 的用户 打开浏览器 访问你的网站( SP),网站 提供服务 可是并 不负责用户认证

  2. 因而 SPIDP 发送了一个 SAML 认证请求,同时 SP用户浏览器 重定向到 IDP

  3. IDP 在验证完来自 SP请求无误 以后,在浏览器中呈现 登录表单 让用户填写 用户名密码 进行登录。

  4. 一旦用户登录成功, IDP 会生成一个包含 用户信息用户名 或者 密码)的 SAML tokenSAML token 又称为 SAMLAssertion,本质上是 XML 节点)。 IDPSP 返回 token, 而且将 用户重定向SP ( token 的返回是在 重定向步骤 中实现的,下面会详细说明)。

  5. SP 对拿到的 token 进行验证,并从中解析出 用户信息,例如 用户是谁 以及 用户的权限 有哪些。此时就可以根据这些信息容许用户访问咱们网站的内容。

当用户在 IDP 登录成功以后, IDP 须要将用户 再次重定向SP 站点,这一步一般有两个办法:

  • HTTP 重定向:这并不推荐,由于 重定向URL 长度 有限制,没法携带更长的信息,好比 SMALToken

  • HTTP POST 请求:这个是更常规的作法,当用户登录完毕以后渲染出一个表单,用户点击后向 SP 提交 POST 请求。又或者可使用 JavascriptSP 发出一个 POST 请求。

若是你的应用是基于 Web,那么以上的方案没有任何问题。但若是你开发的是一个 iOS 或者 Android 的手机应用,那么问题就来了:

  1. 用户在 iPhone 上打开应用,此时用户须要经过 IDP 进行认证。

  2. 应用跳转至 Safari 浏览器,在登录认证完毕以后,须要经过 HTTP POST的形式将 token 返回至 手机应用

虽然 POSTurl 能够 拉起应用,可是 手机应用 没法解析 POST 的内容,咱们也就没法读取 SAMLToken

固然仍是有办法的,好比在 IDP 受权阶段 不跳转至系统的 Safari 浏览器,在 内嵌Webview 中解决,在千方百计从 Webview 中提取 token,或者利用 代理服务器

不管如何, SAML2.0不适用 于当下 跨平台 的场景,这也许与它产生的年代也有关系,它诞生于 2005 年,在那个时刻 HTTP POST 确实是最好的选择方案。

OAuth 2.0

咱们先简单了解 SSO 下的 OAuth2.0 的流程。

  1. 用户经过 客户端(能够是 浏览器 也能够是 手机应用)想要访问 SP 上的资源,可是 SP 告诉用户须要进行 认证,将用户 重定向IDP

  2. IDP用户 询问 SP 是否能够访问 用户信息。若是用户赞成, IDP客户端 返回 authorization code

  3. 客户端拿到 authorization codeIDP 交换 access token,并拿着 access tokenSP 请求资源

  4. SP 接受到请求以后,拿着附带的 tokenIDP 验证 用户的身份。确认身份无误后, SP客户端 发放相关资源。

那么 OAuth 是如何避免 SAML 流程下 没法解析 POST 内容的信息的呢?

  • 一方面是用户从 IDP 返回 客户端 的方式,也是经过 URL 重定向,这里的 URL 容许 自定义 schema,因此即便在 手机 上也能 拉起应用

  • 另外一方面由于 IDP客户端 传递的是 authorization code,而不是 XML信息,因此 code 能够很轻易的附着在 重定向 URL 上进行传递。

但以上的 SSO 流程体现不出 OAuth 的本意。 OAuth 的本意是 一个应用 容许 另外一个应用用户受权 的状况下 访问本身的数据

OAuth 的设计本意更倾向于 受权而非认证(固然受权用户信息就间接实现了认证), 虽然 GoogleOAuth2.0API 同时支持 受权认证。因此你在使用 Facebook 或者 Gmail 帐号登录第三方站点时,会出现 受权对话框,告诉你 *第三方站点 能够访问你的哪些信息,须要征得你的赞成。

在上面 SSOOAuth 流程中涉及三方角色: SP, IDP 以及 Client。但在实际工做中 Client 能够是不存在的,例如你编写了一个 后端程序 定时的经过 GoogleAPIYoutube 拉取最新的节目数据,那么你的 后端程序 须要获得 YoutubeOAuth 受权 便可。

OAuth VS OpenId

若是你有留心的话,你会在某些站点看到容许以 OpenID 的方式登录,其实也就是以 Facebook 帐号或者 Google 帐号登录站点:

OpenIDOAuth 很像。但本质上来讲它们是大相径庭的两个东西:

  • OpenID: 只用于 身份认证Authentication),容许你以 同一个帐户多个网站登录。它仅仅是为你的 合法身份 背书,当你以 Facebook 帐号登录某个站点以后,该站点 无权访问 你的在 Facebook 上的 数据

  • OAuth: 用于 受权Authorisation),容许 被受权方 访问 受权方用户数据

Refresh Token

如今能够回答上面的问题了,为何咱们须要 refresh token

这样的处理是为了 职责的分离

  • refresh token: 负责 身份认证;

  • access token: 负责 请求资源。

虽然 refresh tokenaccess token 都由 IDP 发出,可是 access token还要和 SP 进行 数据交换,若是 公用的话 这样就会有 身份泄露 的可能。而且 IDPSP 多是 彻底不一样服务提供 的。而在上文,咱们之因此没有这样的顾虑是由于 IDPSP 都是 Google

JWT

初步认识

本质上来讲 JWT 也是 token,正如咱们在上文提到的,它是 访问资源凭证

Google 的一些 API 诸如 PredictionAPI 或者 GoogleCloudStorage,是不须要 访问 用户的 我的数据 的。于是不须要通过 用户的受权 这一步骤,应用程序能够直接访问。就像上面 OAuth 中没有 Client 没有参与的流程相似。这就要借助 JWT 完成访问了, 具体流程以下:

  1. 首先须要在 GoogleAPI 上建立一个服务帐号( service account)。

  2. 获取 服务帐号认证信息credential),包括 邮箱地址client ID,以及一对 公钥/私钥

  3. 使用 client ID私钥 创一个 签名JWT,而后将这个 JWT 发送给 Google 交换 access token

  4. Google 返回 access token

  5. 程序经过 access token 访问 API

甚至你能够不须要向 Google 索要 access token,而是携带 JWT 做为 HTTP header 里的 bearer token 直接访问 API 也是能够的。这才是 JWT 的最大魅力。

理性认识

JWT 顾名思义,它是 JSON 结构的 token,由三部分组成:

  1. header

  2. payload

  3. signature

header

header 用于描述 元信息,例如产生 signature 的算法:

  
    
  
  
  
   
   
            
   
   
{    "typ": "JWT",    "alg": "HS256"}

其中 alg 关键字就指定了使用哪种 哈希算法 来建立 signature

payload

payload 用于携带你但愿 向服务端传递 的信息。你既能够往里添加 官方字段,例如: iss(Issuer), sub(Subject)exp(Expirationtime),也能够塞入 自定义的字段,好比 userId:

  
    
  
  
  
   
   
            
   
   
{    "userId": "b08f86af-35da-48f2-8fab-cef3904660bd"}

signature

signature 译为 签名,建立签名要分如下几个步骤:

  1. 接口服务端 拿到 密钥,假设为 secret

  2. header 进行 base64 编码,假设结果为 headerStr

  3. payload 进行 base64 编码,假设结果为 payloadStr

  4. headerStrpayloadStr. 字符 拼装起来成为字符 data

  5. datasecret 做为参数,使用 哈希算法 计算出 签名

若是上述描述还不直观,用 伪代码 表示就是:

  
    
  
  
  
   
   
            
   
   
// signature algorithmdata = base64urlEncode( header ) + “.” + base64urlEncode( payload )signature = Hash( data, secret );

假设咱们的原始 JSON 结构是这样的:

  
    
  
  
  
   
   
            
   
   
// Header{  "typ": "JWT",  "alg": "HS256"}// Payload{  "userId": "b08f86af-35da-48f2-8fab-cef3904660bd"}

若是 密钥 是字符串 secret 的话,那么最终 JWT 的结果就是这样的:

  
    
  
  
  
   
   
            
   
   
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM

能够在 jwt.io验证 这个结果。

JWT究竟带来了什么

确保数据完整性

JWT 的目的不是为了 隐藏 或者 保密数据,而是为了确保 数据 确实来自被 受权的人 建立的,以防止 中途篡改

回想一下,当你拿到 JWT 时候,你彻底能够在没有 secret 的状况下解码出 headerpayload,由于 headerpayload 只是通过了 base64 编码encode)而已,编码的目的在于 利于数据结构的传输

虽然建立 signature 的过程近似于 加密 ( encrypt),但本质实际上是一种 签名 ( sign) 的行为,用于保证 数据的完整性,实际上也而且并 没有加密任何数据

用于接口调用

接下来在 API 调用中就能够附上 JWT (一般是在 HTTPHeader 中)。又由于 SP 会与程序 共享 一个 secret,因此 程序 能够经过 header 提供的相同的 hash 算法来 验证签名 是否正确,从而判断应用是否有权力调用 API

有状态的对话Session

由于 HTTP无状态 的,因此 客户端服务端 须要解决的问题是,如何让它们之间的对话变得有状态。例如只有是 登录状态用户 才有权限调用某些接口,那么在 用户登录 以后,须要记住该用户是 已经登录 的状态。常见的方法是使用 session 机制。

常见的 session 模型是这样工做的:

  1. 用户在浏览器 登录 以后,服务端为用户生成 惟一session id,存储在 服务端存储服务(例如 MySQL, Redis)中。

  2. session id 也同时 返回给浏览器,以 SESSION_IDKEY 存储在浏览器的 cookie 中。

  3. 若是用户再次访问该网站, cookie 里的 SESSION_ID 会随着 请求 一同发往 服务端

  4. 服务端经过判断 SESSION_ID 是否已经在 Redis 中判断用户是否处于 登录状态

相信你已经察觉了,理论上来讲, JWT 机制能够取代 session 机制。用户不须要提早进行登录,后端也不须要 Redis 记录用户的登录信息。客户端的本地保存一份合法的 JWT,当用户须要调用接口时,附带上该合法的 JWT,每一次调用接口,后端都使用请求中附带的 JWT 作一次 合法性的验证。这样也间接达到了 认证用户 的目的。

然而 JWT 真的能取代 session 机制吗?这么作有哪些好处和坏处?这些问题将留在下一篇再讨论。


本文分享自微信公众号 - 风帆(wdswhf)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索