SSO、JWT和Oauth2

Database and Ruby, Python, History


打算讲个故事,把 SSO,OAUTH,JWT 这些都揉在一起。

  1. 2000 年之后,互联网蓬勃发展,小明创建了一个网站 www.xxx.com。因为 HTTP 协议是无状态的,下一次请求我就不知道是谁在请求了,那么用户请求的时候就需要带一个凭证,来维持 session。那个时候,用户一般就是在 www.xxx.com 上面输入账号,密码登录系统,session 信息就写到数据库里面,每次过来我去数据库里面查一下。

  2. 小明的业务发展了几年,发展得不错。他又继续开发新的业务,又创建了新的网站 pricing.xxx.com。新的网站总不能够让用户又重新登录系统吧!这个时候,小明把 Cookie 信息从www.xxx.com域改到xxx.com下,使得Cookie可以在两个网站之间共享。

  3. 用户越来越多,xxx.com 后面的单机服务器不够用了,数据库也扛不住了,需要扩容。一旦扩容,就会涉及 session 共享的问题,否则用户在服务器 1 上登录了,下一次请求到了服务器 2 上就显示为未登录了。小明决定索性就不在服务器上保存用户的 session 了,直接在 Cookie 里面写入用户的信息,服务器上用同样的 salt 去解密这个 Cookie ,拿到用户信息就可以了。

  4. 业务发展得挺不错的,又收购了一家新公司,需要和新公司做集成,实现单点登录,即用户登录了www.xxx.com之后,再访问www.yyy.com的时候,不需要再登录一次。CAS协议可以提供单点登录,也可以理解为把登录认证这一步单独抽取出来。每次登录xxx.com的时候,都去CAS页面登录,登录成功之后,页面跳转回xxx.com并返回一个ticket,xxx.com的后端拿到这个ticket/code之后,去CAS确认一下是不是伪造的。没有问题就把session写到Cookie里面去,当然这个时候这个Cookie的域还是xxx.com的。用户再访问yyy.com的时候,同样会被跳转到CAS,因为CAS已经登录过了,保留了Cookie,又自动跳转回来,写入域为yyy.com的Cookie。如果用户要登出,则需要在xxx.com和yyy.com都要登出。

  5. 后来小明将 CAS 升级为更加通用,安全的 SAML 协议来实现 SSO。这两个方式都是基于浏览器/服务器的方式,因此中间涉及到几次的页面跳转。

  6. 基于 Cookie 的 session 有一个缺点,就是需要指定域名。当用户通过非浏览器客户端访问另外一个 API 的时候,不会带上 session 信息的。这个时候需要一个更加通用的跨域认证方案,即 JWT。JWT 里面可以通过 base64 算法转换出来里面的内容,从而获取到用户的信息。

  7. 同时,由于移动端的蓬勃发展,不是每个第三方都有网站,更多的是手机应用。传统的 SAML 不再适用于这种场景,转而使用 OAUTH2 方式。Oauth2 有 4 种授权方式,分别是授权式,隐藏式,密码式和凭证式,比较常见的方式是授权码方式。和上面的 SAML 类似,用户被跳转到一个页面,输入账号密码,返回 code,后端拿着这个 code 去兑换 token。

如果是手机应用,可以使用密码式,即用户输入账号密码,应用拿着账号密码去换取 token。这里就没有浏览器的跳转了。

token 是一个短期内有效的访问凭证,可以通过该凭证访问指定的资源。