软件设计 2017-05-30
认证授权系列:http://www.cnblogs.com/linianhui/category/929878.html
看一下官方的介绍(http://openid.net/connect/):
[blockquote]
OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and session management, when it makes sense for them.
[/blockquote]
简单来说:OIDC是OpenID Connect的简称,OIDC=(Identity, Authentication) + OAuth 2.0。它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议。我们都知道OAuth2是一个授权协议,它无法提供完善的身份认证功能(关于这一点请参考[认证授权] 3.基于OAuth2的认证(译)),OIDC使用OAuth2的授权服务器来为第三方客户端提供用户的身份认证,并把对应的身份认证信息传递给客户端,且可以适用于各种类型的客户端(比如服务端应用,移动APP,JS应用),且完全兼容OAuth2,也就是说你搭建了一个OIDC的服务后,也可以当作一个OAuth2的服务来用。应用场景如图:
OIDC已经有很多的企业在使用,比如Google的账号认证授权体系,Microsoft的账号体系也部署了OIDC,当然这些企业有的也是OIDC背后的推动者。除了这些之外,有很多各个语言版本的开源服务端组件,客户端组件等等(http://openid.net/developers/certified/);
理解OIDC的前提是需要理解OAuth2,这里假设大家都有OAuth2的基础,不清楚的可以先阅读本系列的前几篇OAuth2的文章。
OIDC本身是有多个规范构成,其中包含一个核心的规范,多个可选支持的规范来提供扩展支持,简单的来看一下:
除了上面这8个之外,还有其他的正在制定中的扩展。看起来是挺多的,不要被吓到,其实并不是很复杂,除了Core核心规范内容多一点之外,另外7个都是很简单且简短的规范,另外Core是基于OAuth2的,也就是说其中很多东西在复用OAuth2,所以说你理解了OAuth2之后,OIDC就是非常容易理解的了,我们这里就只关注OIDC引入了哪些新的东西(Core,其余7个可选规范不做介绍,但是可能会提及到)。千言万语都不如一张图,没图你说个***。
上图是官方给出的一个OIDC组成结构图,我们暂时只关注Core的部分,其他的部分了解是什么东西就可以了,当作黑盒来用。就像当初的AJAX一样,它其实并不是一个新的技术,而是结合很多已有的技术,按照规范的方式组合起来,就是AJAX。同理,OIDC也不是新技术,它主要是借鉴OpenId的身份标识,OAuth2的授权和JWT包装数据的方式,把这些技术融合在一起就是OIDC。
OAuth2提供了Access Token来解决授权第三方客户端访问受保护资源的问题;OIDC在这个基础上提供了ID Token来解决第三方客户端标识用户身份认证的问题。OIDC的核心在于在OAuth2的授权流程中,一并提供用户的身份认证信息(ID Token)给到第三方客户端,ID Token使用JWT格式来包装,得益于JWT(JSON Web Token)的自包含性,紧凑性以及防篡改机制,使得ID Token可以安全的传递给第三方客户端程序并且容易被验证。此外还提供了UserInfo的接口,用户获取用户的更完整的信息。
主要的术语以及概念介绍(完整术语参见http://openid.net/specs/openid-connect-core-1_0.html#Terminology):
从抽象的角度来看,OIDC的流程由以下5个步骤构成:
上图取自Core规范文档,其中AuthN=Authentication,表示认证;AuthZ=Authorization,代表授权。注意这里面RP发往OP的请求,是属于Authentication类型的请求,虽然在OIDC中是复用OAuth2的Authorization请求通道,但是用途是不一样的,且OIDC的AuthN请求中scope参数必须要有一个值为的openid的参数(后面会详细介绍AuthN请求所需的参数),用来区分这是一个OIDC的Authentication请求,而不是OAuth2的Authorization请求。
上面提到过OIDC对OAuth2最主要的扩展就是提供了ID Token。ID Token是一个安全令牌,是一个授权服务器提供的包含用户信息(由一组Cliams构成以及其他辅助的Cliams)的JWT格式的数据结构。ID Token的主要构成部分如下(使用OAuth2流程的OIDC)。
ID Token通常情况下还会包含其他的Claims(毕竟上述claim中只有sub是和EU相关的,这在一般情况下是不够的,必须还需要EU的用户名,头像等其他的资料,OIDC提供了一组公共的cliams,请移步这里http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)。另外ID Token必须使用JWS进行签名和JWE加密,从而提供认证的完整性、不可否认性以及可选的保密性。一个ID Token的例子如下:
{ "iss": "https://server.example.com", "sub": "24400320", "aud": "s6BhdRkqt3", "nonce": "n-0S6_WzA2Mj", "exp": 1311281970, "iat": 1311280970, "auth_time": 1311280969, "acr": "urn:mace:incommon:iap:silver" }
解释完了ID Token是什么,下面就看一下OIDC如何获取到ID Token,因为OIDC基于OAuth2,所以OIDC的认证流程主要是由OAuth2的几种授权流程延伸而来的,有以下3种:
[blockquote]
这里有个小问题大家可以思考下,OAuth2中还有基于Resource Owner Password Credentials Grant和Client Credentials Grant的方式来获取Access Token,为什么OIDC没有扩展这些方式呢?
Resource Owner Password Credentials Grant是需要用途提供账号密码给RP的,账号密码给到RP了,还要什么自行车(ID Token)。。。
Client Credentials Grant这种方式根本就不需要用户参与,更谈不上用户身份认证了。这也能反映授权和认证的差异,以及只使用OAuth2来做身份认证的事情是远远不够的,也是不合适的。
[/blockquote]
这种方式使用OAuth2的Authorization Code的方式来完成用户身份认证,所有的Token都是通过Token EndPoint(OAuth2中定义:https://tools.ietf.org/html/rfc6749#section-3.2)来发放的。构建一个OIDC的Authentication Request需要提供如下的参数:
以上这5个参数是和OAuth2相同的。除此之外,还定义了如下的参数:
以上是基于Authorization Code方式的OIDC的认证请求所需的参数。在OIDC的其他认证流程中也会有其他的参数或不同的参数值(稍有差异)。一个简单的示例如下:
[blockquote]
GET /authorize? response_type=code &scope=openid%20profile%20email &client_id=s6BhdRkqt3 &state=af0ifjsldkj &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1 Host: server.example.com
[/blockquote]
也可以是一个基于302的重定向方式。
在授权服务器接收到认证请求之后,需要对请求参数做严格的验证,具体的规则参见http://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation,验证通过后引导EU进行身份认证并且同意授权。在这一切都完成后,会重定向到RP指定的回调地址,并且把code和state参数传递过去。比如:
[blockquote]
HTTP/1.1 302 Found Location: https://client.example.org/cb? code=SplxlOBeZQQYbYS6WxSbIA &state=af0ifjsldkj
[/blockquote]
RP使用上一步获得的code来请求Token EndPoint,这一步同OAuth2,就不再展开细说了。然后Token EndPoint会返回响应的Token,其中除了OAuth2规定的部分数据外,还会附加一个id_token的字段。id_token字段就是上面提到的ID Token。例如:
[blockquote]
HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache { "access_token": "SlAV32hkKG", "token_type": "Bearer", "refresh_token": "8xLOxBtZp8", "expires_in": 3600, "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5 NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4 XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg" }
[/blockquote]
其中看起来一堆乱码的部分就是JWT格式的ID Token。在RP拿到这些信息之后,需要对id_token以及access_token进行验证(具体的规则参见http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation和http://openid.net/specs/openid-connect-core-1_0.html#ImplicitTokenValidation)。至此,可以说用户身份认证就可以完成了,后续可以根据UserInfo EndPoint获取更完整的信息。
Implicit Flow的工作方式是在OAuth2 Implicit Flow上附加提供id_token,当然,认证请求的参数和基于Authorization Code的流程稍有不同,具体的差异参见http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest,这里就不做详细介绍了。
Hybrid Flow则=Authorization Code Flow+Implicit Flow,也不再详细介绍了。
UserIndo EndPoint是一个受OAuth2保护的资源。在RP得到Access Token后可以请求此资源,然后获得一组EU相关的Claims,这些信息可以说是ID Token的扩展,比如如果你觉得ID Token中只需包含EU的唯一标识sub即可(避免ID Token过于庞大),然后通过此接口获取完整的EU的信息。此资源必须部署在TLS之上,例如:
[blockquote]
GET /userinfo HTTP/1.1 Host: server.example.com Authorization: Bearer SlAV32hkKG
[/blockquote]
成功之后响应如下:
[blockquote]
HTTP/1.1 200 OK Content-Type: application/json { "sub": "248289761001", "name": "Jane Doe", "given_name": "Jane", "family_name": "Doe", "preferred_username": "j.doe", "email": "[email protected]", "picture": "http://example.com/janedoe/me.jpg" }
[/blockquote]
其中sub代表EU的唯一标识,这个claim是必须的,其他的都是可选的。
继OAuth2之后,感觉OIDC也要大放异彩了。其本身是一个完全开放的标准,而且兼容众多的已有的IDP(身份提供商),比如基于SAML的、基于WS-Federation的等等已有的身份认证系统,都可以作为OIDC的OP存在;比如前些日子笔者写的一个demo(基于OIDC的一个.NET版本的开源实现IdentityServer3),使用了QQ互联作为OP来进行身份认证。总结一下OIDC有那些特性和好处吧:
以上内容均是个人的一些理解,如果错误之处,欢迎指正!
官方资料:
http://openid.net/connect/
http://openid.net/connect/faq/
http://openid.net/developers/certified/
JWT : https://tools.ietf.org/html/rfc7519
JWS:https://tools.ietf.org/html/rfc7515
JWE:https://tools.ietf.org/html/rfc7516
.NET的开源实现:https://github.com/IdentityServer
笔者基于IdentityServer3写的一个demo:https://github.com/linianhui/Ids3.demo
视频:Identity, Authentication + OAuth = OpenID Connect
案例:
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-openid-connect-code
https://developers.google.com/identity/protocols/OpenIDConnect