本文首发于我的博客 https://teobler.com 转载请注明出处
SSO是什么
在了解SSO是什么之前,我们需要搞清楚两个概念: Authentication & Authorization
。
Authentication(又被称为AuthN,身份验证),它指的是 the process of verifying that "you are who you say you are"
,也就是说这个过程是为了证明你是你。通常来说有这么几个方式:
- Single-factor - 也就是可以通过单一的因素证明”你是你“,比如密码、PIN码
- Multi-factor - 有时候
Single-factor
没有办法保证”你是你“,就会需要一些多重验证的手段,比如动态口令、生物识别等 - 上面提到的是两种用的比较多的手段,还有一些其他的,比如安全问题,短信,email认证等等
Authorization(又被称为AuthZ,权限验证),他指的是 the process of verifying that "you are permitted to do what you are trying to do"
,也就是说这个过程是为了证明你是否拥有做这件事的权限,比如修改某个表格等等,如果没有权限的话通常会返回 403
错误码。
在 SSO
出现之前,用户在不同的系统登录就需要在不同的系统注册多个账号,然后需要自己记住多个用户名和密码,而如果这些系统是同一个平台的话,其实该平台还需要维护多套几乎一模一样的登录系统,给用户和平台都带来了负担。
而 SSO
就是一种authentication scheme(身份验证方案),SSO
允许用户使用同一个账户登录不同的系统,很好地解决了上述的问题。它不但可以实现单一平台的登录,假如某个平台之外的系统是信任该平台的,那么外部系统也可以集成这个平台的 SSO
, 比如现在的大多数网站都提供了 Google
账号登录的功能。
要实现 SSO
,首先需要你正在开发的系统信任这个第三方登录系统所提供的用户信息,然后你需要按照一定的标准和协议去与其对接,接下来我们会介绍两个比较常用的 SSO
协议 -- SAML 2.0 和 OIDC。
SAML 2.0
SAML 2.0是什么
SAML
是 Security Assertion Markup Language
的简称,是一种基于XML的开放标准协议,用于在身份提供者(Identity Provider简称IDP)和服务提供商(Service Provider简称SP)之间交换认证和授权数据。
SAML 2.0是该协议的最新版本,于2005年被结构化信息标准组织(OASIS)批准实行。
流程
- 用户要访问SP上面的资源,但是SP要求提供用户身份信息
- SP 会将用户跳转到 IDP,IDP 返回一个登陆页面
- 用户成功登陆后IDP会提供一个
SAML Assertion
,通过用户端传递给SP - 此时SP会验证这个
SAML Assertion
,没有问题的话就会返回相应的资源 - 如果后面用户又要访问该平台的另外一个系统,由于该用户已经在IDP那边登录过了,所以此次访问IDP就能够直接将用户的
SAML Assertion
传递给这个新的SP,便可以实现不登录直接访问资源
SAML Assertion
那么这个 SAML Assertion
又是个啥呢?
首先用户为什么可以访问SP上的资源呢?肯定是因为其实SP也有一份用户的资料,这个资料里面可能有用户的账户信息,权限等等,但是现在用户的信息是由IDP提供的,所以现在需要做的就是讲两份用户信息映射起来,好让SP知道IDP提供的用户到底是哪一个。
而这个映射的约定,就是SP和IDP进行集成的时候的一个配置,这个配置叫做 metadata
。这个配置有两份,两边各一份,里面约定了应该怎么去映射用户信息,签名的证书等。IDP和SP会通过别的方式去交换这两份 metadata
。
所以其实 SAML Assertion
里面包含了用户的唯一标识,能够证明该用户是谁。在SP拿到这份信息后就会按照一些规则去验证里面的信息是否是合法的用户。
那么问题来了,如果中间人知道了我们之间的规则随便塞了一份信息进来咋办?所以其实 SAML Assertion
里面除了用户信息其实还有IDP的签名,只有SP先解析了里面的签名确认无误之后才会信任这份信息。
知道了 SAML Assertion
是个啥以后,我们还需要弄清楚它是怎么发送出去的。要弄清楚它们是怎么发送出去的我们需要知道一个东西叫做 binding method
。
SAML 2.0 有许多不同的 binding
,它们其实就是 SAML Assertion
的交互方式:
- HTTP redirect binding
- HTTP POST binding
- HTTP artifact binding
- SAML URI binding
- SAML SOAP binding(based on SOAP 1.1)
- reverse SOAP(PAOS) binding
其中现在用的比较多的是前三种,它们都是基于HTTP协议来实现的。
- redirect binding是在SP redirect到IDP的时候会在URL中带上请求信息,比如id,谁发出来的等等
- POST binding是为了解决redirect方式在使用过程中的一些问题而产生的,比如URL过长,response不安全等等
- artifact可以看做一个引用,这个引用会通过浏览器带到SP那边,SP拿到之后再通过里面的信息去IDP拿相应的
SAML Assertion
metadata
上面提到过 metadata
是为了让IDP和SP明白彼此交流的信息,并且有一些安全考虑,里面主要的信息有:
- NameFormat -- 约定用户ID的格式,比如 email address, transient等等
- Certificate -- 解析签名,加密assertion
- Entity identifier -- 该metadata的唯一标识符
- Binding -- 使用何种方式通信
其中有一个字段 md:KeyDescriptor
在SP中有一个 encryption
,在SP和IDP进行通信建立信任的时候,IDP就会拿到SP加密的key,在用户登录成功后,IDP就会用这个key加密 SAML Assertion
,SP拿到后通过自己的私钥进行解密。另一个叫 signing
的字段会被用来解析对方的签名,用来辨别这个 Assertion 是不是我想要的人发过来的。
OIDC
OIDC是什么
OpenID Connect(OIDC) 是建立在 OAuth 2.0 协议之上的一个简单的身份层,它允许计算客户端根据授权服务器执行的认证,以 JSON 作为数据格式,验证终端用户的身份。它是 OpenID 的第三代规范,前面分别有 OpenID 和 OpenID 2.0。它在OAuth 2.0 的基础上增加了 ID Token 来解决第三方客户端标识用户身份认证的问题。
它的结构如图所示:
从它的结构图中可以看出,除了核心实现外,OIDC 还提供了一系列可选的扩展功能。比如:
- Discovery:发现服务,使客户端可以动态的获取 OIDC 服务相关的元数据描述信息(比如支持那些规范,接口地址是什么等等)
- Dynamic Registration :可选。动态注册服务,使客户端可以动态的注册到OIDC的OP
- Session Management :Session管理,用于规范OIDC服务如何管理Session信息
- OAuth 2.0 Form Post Response Mode:针对 OAuth2 的扩展,OAuth2 回传信息给客户端是通过URL的 querystring 和 fragment 这两种方式,这个扩展标准提供了一基于 form 表单的形式把数据 post 给客户端的机制
由于图片距今已经有些年限了,其实现在OIDC还提供了许多可选的扩展,具体可到官网查看。
流程
由于 OIDC 是基于 OAuth 2.0 的,所以 OIDC 也拥有多种 flow。由于篇幅所限我这里会相对详细地解释 Authorization Code Flow,在开始前我们需要弄清楚几个名称:
- EU:End User,指使用终端(浏览器等)访问服务器资源的用户
- RP:Relying Party,用来代指 OAuth2 中的受信任的客户端,身份认证和授权信息的消费方,相当于 SAML 中的 SP
- OP:OpenID Provider,有能力提供EU认证的服务(比如OAuth2中的授权服务),用来为RP提供EU的身份认证信息,相当于 SAML 中的 IDP
-
ID Token:JWT格式的数据,包含 EU 身份认证的信息。ID Token 由 JWS 进行签名和 JWE 加密,从而提供认证的完整性、不可否认性以及可选的保密性。里面可能会有很多字段,详细可以看这里,其中这几个字段是一定包含其中的
- iss - Issuer Identifier:提供认证信息者的唯一标识,通常是一个 HTTPS 的 URL
- sub - Subject Identifier:iss 提供的 EU 的标识,在 iss 范围内唯一,它会被 RP 用来标识唯一的用户,最长为255个ASCII个字符
- aud - Audience(s):标识ID Token的受众,必须包含 OAuth 2.0 的client_id
- exp - Expiration time:过期时间,超过此时间的 ID Token 会作废不再被验证通过
- iat - Issued At Time:JWT的构建的时间
- UserInfo Endpoint:用户信息接口(受OAuth2保护),当RP使用Access Token访问时,返回授权用户的信息,此接口必须使用HTTPS
- APP Token:通常来说 OP 提供的用户信息和 Access Token 中包含的信息不带有用户在 RP 中的权限,RP 通常会自己生成一个 token 给 EU 作为后续访问资源的用户证明
Authorization Code flow
- EU 访问 RP 的资源但是没有进行身份认证
-
RP 将 EU redirect 到 OP 端,并带上一些参数,这里列举一些必选参数,还有许多可选参数可以看这里
- client_id:唯一标识
- scope:请求权限范围,OIDC的请求必须包含值为“openid”的scope的参数
- response_type:要求 OP 的返回值,值为
code
,token
,id_token
,none
中的一个或几个,在当前 flow 值为code
- redirect URL:认证完成后的跳转URL
- state:当前登录认证操作的一个随机 query,用于防止 CSRF 或 XSRF 攻击
- 然后 OP 会验证 EU 的身份信息,通常会询问用户是否将自己的信息提供给 RP,确认后进行登录操作
- 登陆成功后 OP 会将 EU redirect 到刚刚 RP 提供的 URL,同时会在 URL 中带上一个 Authorization Code 和刚刚的 state 参数
- 之后 RP 拿到 code 和 state,先确认是不是相同的 state 保证这次通信是有效的,之后再通过 POST 请求从 OP 获取 token,里面包含 ID Token,Access Token,Refresh Token,Token Type,Expired In 等信息
- 之后 RP 会验证 ID Token 和验证 Access Token 确保它们没有问题
- 然后 RP 通过 Access token 通过 OP 提供的 UserInfo Endpoint 获取用户信息,拿到用户信息后与自己的用户信息进行比对
- 最后返回一个 APP Token 到 EU
Implicit Flow
Implicit Flow 是在 OP redirect EU 到 RP 的时候会带上 ID Token 和 Access Token(如果必要) 而不是 Authorization Code,同时在发送请求的时候也会有一些不同,需要带上一些别的参数,这里就不细讲了,总的流程是差不多的,详情可以查看这里
Hybrid Flow
Hybrid Flow 可以理解为上面两个 flow 的结合,OP redirect EU 到 RP 的时候会带上 Authorization Code,同时根据发送请求时候 Response Type 参数的不同还会带上一些别的参数,具体流程可以参考这里
非常感谢你能看到这里,如果你觉得有帮助到你,可以关注我的微信公众号