实现 OAuth2 单点登录SSO—单点登录和OAuth2概述

什么是单点登录

单点登录(Single Sign On)简称为SSO,在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。比方说阿里巴巴有淘宝、天猫、聚划算等应用网站,而他们的登录系统却有且只有一个,无论是在哪个应用跳转到登录页面用户只要输入用户名(邮箱或手机号)和密码登录之后都能在其它几个应用之间相互跳转而无需再次登录,也就是说你只需要注册一个账号可以访问淘宝、天猫或聚划算应用了,而不需要再在淘宝上面注册一个账号,天猫上面注册一个账号或者在聚划算上面注册一个账号了,想想是不是非常爽!

OAuth2

SSO是一种思想,或者说是一种解决方案,使用的是OAuth2相关一整套流程去实现的。

OAuth2.0 定义了四种角色

  • resource owner(资源所有者,也就是我们亲爱的用户自己)
  • resource server(资源服务器,也就是用户自己的资源所在的服务器)
  • client(客户端,浏览器)
  • authorization server(授权服务器,也就是认证和授权的地方)

OAuth运行流程

该图展示的是四种角色之间的关联关系,如果你弄懂了这张图,那么你就完成了一半。下面我来解释下这张图包含的意义:
(A)  客户端向资源所有者(用户)请求其授权

说人话:就是用户名和密码登录,验证用户的意思

(B)  客户端收到资源所有者(用户)的授权许可,这个授权许可是一个代表资源所有者授权的凭据

说人话:客户端得到用户的凭据也就是用户名和密码

(C)  客户端向授权服务器请求访问令牌,并出示授权许可

说人话:用户请求令牌token,并把自己的用户名和密码给了授权服务器

(D)  授权服务器对客户端身份进行认证,并校验授权许可,如果都是有效的,则发放访问令牌

说人话:授权服务器证明了你就是你自己之后就发放了令牌token

(E)  客户端向资源服务器请求受保护的资源,并出示访问令牌

说人话:用户通过授权服务器返回的token 访问了自己的资源

(F)  资源服务器校验访问令牌,如果令牌有效,则提供服务

说人话:资源服务器看到是你的令牌就把你的相关数据信息返回给客户端

上面讲解了客户端获取了用户凭据再通过授权服务器返回的token访问自己的资源服务这样的一个流程,下面我们就来讲解一下,授权服务器通过什么方式返回token给客户端?

OAuth 2.0 规定了四种获得令牌的流程

  • 授权码(authorization-code)
  • 密码式(password)
  • 客户端凭证(client credentials)
  • 隐藏式(implicit)

第一种授权方式:授权码(authorization-code)

概念:授权码方式,指的是第三方应用去授权服务端先申请一个code,然后再用该code获取令牌token。
使用场景:多应用

(A)  客户端通过将资源所有者的用户代理指向授权端点来启动这个流程。客户端包含它的客户端标识符,请求范围,本地状态,和重定向URI,在访问被允许(或者拒绝)后授权服务器立即将用户代理返回给重定向URI。
(B)  授权服务器验证资源所有者(通过用户代理),并确定资源所有者是否授予或拒绝客户端的访问请求。
(C)  假设资源所有者授权访问,那么授权服务器用之前提供的重定向URI(在请求中或在客户端时提供的)将用户代理重定向回客户端。重定向URI包括授权码和前面客户端提供的任意本地状态。
(D)  客户端用上一步接收到的授权码从授权服务器的令牌端点那里请求获取一个访问令牌。
(E)  授权服务器对客户端进行认证,校验授权码,并确保这个重定向URI和第三步(C)中那个URI匹配。如果校验通过,则发放访问令牌,以及可选的刷新令牌。

上面流程中具体需要发送和返回的请求参数写法,下面分解成四步来说明:

Authorization Request

  • response_type:必须的。值必须是"code"。
  • client_id:必须的。客户端标识符。
  • redirect_uri:可选的,验证成功之后返回的url。
  • scope:可选的。请求访问的范围。
  • state:推荐的。一个不透明的值用于维护请求和回调之间的状态。授权服务器在将用户代理重定向会客户端的时候会带上该参数。

客户端会发送类似这样的一个请求给授权服务端:

GET /authorize?response_type=code&client_id=s6BhdRkqt3&redirect_uri=https://client.example.com/cb&state=xyz
HTTP/1.1  
Host: server.example.com

Authorization Response

  • code:必须的。授权服务器生成的授权码。授权代码必须在发布后不久过期,以减少泄漏的风险。建议最大授权代码生命期为10分钟。客户端不得多次使用授权代码。如果授权代码不止一次使用,授权服务器必须拒绝请求,并在可能的情况下撤销先前基于该授权代码发布的所有令牌。授权代码是绑定到客户端标识符和重定向URI上的。
  • state:如果之前客户端授权请求中带的有"state"参数,则响应的时候也会带上该参数。

客户端会收到授权服务端发送过来这样的一个请求:

HTTP/1.1 302 Found  
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz

Access Token Request

  • grant_type:必须的。值必须是"authorization_code"。
  • code:必须的。值是从授权服务器那里接收的授权码。
  • redirect_uri:如果在授权请求的时候包含"redirect_uri"参数,那么这里也需要包含"redirect_uri"参数。而且,这两处的"redirect_uri"必须完全相同。
  • client_id:如果客户端不需要认证,那么必须带的该参数。
  • client_secret
POST /token HTTP/1.1  
Host: server.example.com  
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https://client.example.com/cb

说明:上面的Authorization是用户通过工具转换的,如果不加这个,用户必须在链接里面加上client_id=xxx&client_secret=xxx

Access Token Response

这样授权服务器就返回token给客户端了:

{
    "access_token":"2YotnFZFEjr1zCsicMWpAA",
    "token_type":"example",
    "expires_in":3600,
    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
    "example_parameter":"example\_value"
  }

第二种授权方式:密码式(password)

概念:如果你高度信任某个应用,用户直接把用户名和密码告诉该应用。该应用就使用你的密码来申请令牌,这种方式称为"密码式"。
使用场景:单应用

(A)  资源所有者提供他的用户名和密码给客户端
(B)  客户端携带从资源所有者那里收到的凭证去授权服务器的令牌端点那里请求获取访问令牌
(C)  授权服务器对客户端进行身份认证,并校验资源所有者的凭证,如果都校验通过,则发放访问令牌

上面流程中具体需要发送和返回的请求参数写法,下面分解成两步来说明:

Access Token Request

  • grant_type :必须的。而且值必须是"password"。
  • username :必须的。资源所有者的用户名。
  • password :必须的。资源所有者的密码。
  • scope:可选的。
POST /token HTTP/1.1  
Host: server.example.com  
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW  
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w

说明:上面的Authorization是用户通过工具转换的,如果不加这个,用户必须在链接里面加上client_id=xxx&client_secret=xxx

Access Token Response

HTTP/1.1 200 OK  
Content-Type: application/json;charset=UTF-8  
Cache-Control: no-store  
Pragma: no-cache

{  
 "access_token":"2YotnFZFEjr1zCsicMWpAA",  
 "token_type":"example",  
 "expires_in":3600,  
 "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",  
 "example_parameter":"example\_value"  
}

本文主要讲解第一种授权方式:授权码(authorization-code),像密码式、客户端凭证和隐藏式大家可以自行百度了解。

参考

OAuth 2.0 的四种方式
理解OAuth 2.0
OAuth2实现单点登录SSO
OAuth 2.0详解

阅读 696

推荐阅读
全栈工程师进阶
用户专栏

日常学习总结与分享,包括:前端、后台与运维,讲解的知识点包括:javascript、vuejs、reactjs、springb...

71 人关注
38 篇文章
专栏主页