如果要談單點(diǎn)登錄和身份認(rèn)證,就不得不談OpenID Connect (OIDC)。最典型的使用實(shí)例就是使用Google賬戶登錄其他應(yīng)用,這一經(jīng)典的協(xié)議模式,為其他廠商的第三方登錄起到了標(biāo)桿的作用,被廣泛參考和使用。
相關(guān)閱讀:
OAuth2.0 協(xié)議入門指南
SAML2.0入門指南
1. OpenID Connect簡(jiǎn)介
OpenID Connect是基于OAuth 2.0規(guī)范族的可互操作的身份驗(yàn)證協(xié)議。它使用簡(jiǎn)單的REST / JSON消息流來(lái)實(shí)現(xiàn),和之前任何一種身份認(rèn)證協(xié)議相比,開(kāi)發(fā)者可以輕松集成。
OpenID Connect允許開(kāi)發(fā)者驗(yàn)證跨網(wǎng)站和應(yīng)用的用戶,而無(wú)需擁有和管理密碼文件。OpenID Connect允許所有類型的客戶,包括基于瀏覽器的JavaScript和本機(jī)移動(dòng)應(yīng)用程序,啟動(dòng)登錄流動(dòng)和接收可驗(yàn)證斷言對(duì)登錄用戶的身份。
2. OpenID的歷史是什么?
OpenID Connect是OpenID的第三代技術(shù)。首先是原始的OpenID,它不是商業(yè)應(yīng)用,但讓行業(yè)領(lǐng)導(dǎo)者思考什么是可能的。OpenID 2.0設(shè)計(jì)更為完善,提供良好的安全性保證。然而,其自身存在一些設(shè)計(jì)上的局限性,最致命的是其中依賴方必須是網(wǎng)頁(yè),但不能是本機(jī)應(yīng)用程序;此外它還要依賴XML,這些都會(huì)導(dǎo)致一些應(yīng)用問(wèn)題。
OpenID Connect的目標(biāo)是讓更多的開(kāi)發(fā)者使用,并擴(kuò)大其使用范圍。幸運(yùn)的是,這個(gè)目標(biāo)并不遙遠(yuǎn),現(xiàn)在有很好的商業(yè)和開(kāi)源庫(kù)來(lái)幫助實(shí)現(xiàn)身份驗(yàn)證機(jī)制。
3. OIDC基礎(chǔ)
簡(jiǎn)要而言,OIDC是一種安全機(jī)制,用于應(yīng)用連接到身份認(rèn)證服務(wù)器(Identity Service)獲取用戶信息,并將這些信息以安全可靠的方法返回給應(yīng)用。
在最初,因?yàn)镺penID1/2經(jīng)常和OAuth協(xié)議(一種授權(quán)協(xié)議)一起提及,所以二者經(jīng)常被搞混。
- OpenID是Authentication,即認(rèn)證,對(duì)用戶的身份進(jìn)行認(rèn)證,判斷其身份是否有效,也就是讓網(wǎng)站知道“你是你所聲稱的那個(gè)用戶”;
-
OAuth是Authorization,即授權(quán),在已知用戶身份合法的情況下,經(jīng)用戶授權(quán)來(lái)允許某些操作,也就是讓網(wǎng)站知道“你能被允許做那些事情”。
由此可知,授權(quán)要在認(rèn)證之后進(jìn)行,只有確定用戶身份只有才能授權(quán)。
(身份驗(yàn)證)+ OAuth 2.0 = OpenID Connect
OpenID Connect是“認(rèn)證”和“授權(quán)”的結(jié)合,因?yàn)槠浠?em>OAuth協(xié)議,所以OpenID-Connect協(xié)議中也包含了client_id、client_secret還有redirect_uri等字段標(biāo)識(shí)。這些信息被保存在“身份認(rèn)證服務(wù)器”,以確保特定的客戶端收到的信息只來(lái)自于合法的應(yīng)用平臺(tái)。這樣做是目的是為了防止client_id泄露而造成的惡意網(wǎng)站發(fā)起的OIDC流程。
舉個(gè)例子。某個(gè)用戶使用Facebook應(yīng)用“What online quiz best describes you?” ,該應(yīng)用可以通過(guò)Facebook賬號(hào)登錄,則你可以在應(yīng)用中發(fā)起請(qǐng)求到“身份認(rèn)證服務(wù)器”(也就是Facebook的服務(wù)器)請(qǐng)求登錄。這時(shí)你會(huì)看到如下界面,詢問(wèn)是否授權(quán)。

在OAuth中,這些授權(quán)被稱為scope。OpenID-Connect也有自己特殊的scope--openid ,它必須在第一次請(qǐng)求“身份鑒別服務(wù)器”(Identity Provider,簡(jiǎn)稱IDP)時(shí)發(fā)送過(guò)去。
4. OIDC流程
OAuth2提供了Access Token來(lái)解決授權(quán)第三方客戶端訪問(wèn)受保護(hù)資源的問(wèn)題;相似的,OIDC在這個(gè)基礎(chǔ)上提供了ID Token來(lái)解決第三方客戶端標(biāo)識(shí)用戶身份認(rèn)證的問(wèn)題。OIDC的核心在于在OAuth2的授權(quán)流程中,一并提供用戶的身份認(rèn)證信息(ID-Token)給到第三方客戶端,ID-Token使用JWT格式來(lái)包裝,得益于JWT(JSON Web Token)的自包含性,緊湊性以及防篡改機(jī)制,使得ID-Token可以安全的傳遞給第三方客戶端程序并且容易被驗(yàn)證。應(yīng)有服務(wù)器,在驗(yàn)證ID-Token正確只有,使用Access-Token向UserInfo的接口換取用戶的更多的信息。
有上述可知,OIDC是遵循OAuth協(xié)議流程,在申請(qǐng)Access-Token的同時(shí),也返回了ID-Token來(lái)驗(yàn)證用戶身份。
4.1 相關(guān)定義
- EU:End User,用戶。
- RP:Relying Party ,用來(lái)代指OAuth2中的受信任的客戶端,身份認(rèn)證和授權(quán)信息的消費(fèi)方;
- OP:OpenID Provider,有能力提供EU身份認(rèn)證的服務(wù)方(比如OAuth2中的授權(quán)服務(wù)),用來(lái)為RP提供EU的身份認(rèn)證信息;
- ID-Token:JWT格式的數(shù)據(jù),包含EU身份認(rèn)證的信息。
- UserInfo Endpoint:用戶信息接口(受OAuth2保護(hù)),當(dāng)RP使用ID-Token訪問(wèn)時(shí),返回授權(quán)用戶的信息,此接口必須使用HTTPS。
下面我們來(lái)看看OIDC的具體協(xié)議流程。
根據(jù)應(yīng)用客戶端的不同,OIDC的工作模式也應(yīng)該是不同的。和OAuth類似,主要看是否客戶端能保證client_secret的安全性。
如果是JS應(yīng)用,其所有的代碼都會(huì)被加載到瀏覽器而暴露出來(lái),沒(méi)有后端可以保證client_secret的安全性,則需要是使用默認(rèn)模式流程(Implicit Flow)。
如果是傳統(tǒng)的客戶端應(yīng)用,后端代碼和用戶是隔離的,能保證client_secret的不被泄露,就可以使用授權(quán)碼模式流程(Authentication Flow)。
此外還有混合模式流程(Hybrid Flow),簡(jiǎn)而言之就是以上二者的融合。
OAuth2中還有口令模式和“應(yīng)有訪問(wèn)模式”的方式來(lái)獲取Access Token(關(guān)于OAuth2的內(nèi)容,可以參見(jiàn)OAuth2.0 協(xié)議入門指南),為什么OIDC沒(méi)有擴(kuò)展這些方式呢?
"口令模式"是需要用戶提供賬號(hào)和口令給RP的,既然都已經(jīng)有用戶名和口令了,就不需要在獲取什么用戶身份了。至于“應(yīng)有訪問(wèn)模式”,這種方式不需要用戶參與,也就無(wú)需要認(rèn)證和獲取用戶身份了。這也能反映授權(quán)和認(rèn)證的差異,以及只使用OAuth2來(lái)做身份認(rèn)證的事情是遠(yuǎn)遠(yuǎn)不夠的,也是不合適的。
4.2 授權(quán)碼模式流程

和OAuth認(rèn)證流程類似
- RP發(fā)送一個(gè)認(rèn)證請(qǐng)求給OP,其中附帶client_id;
- OP對(duì)EU進(jìn)行身份認(rèn)證;
- OP返回響應(yīng),發(fā)送授權(quán)碼給RP;
- RP使用授權(quán)碼向OP索要ID-Token和Access-Token,RP驗(yàn)證無(wú)誤后返回給RP;
- RP使用Access-Token發(fā)送一個(gè)請(qǐng)求到UserInfo EndPoint; UserInfo EndPoint返回EU的Claims。
4.2.1 基于Authorization Code的認(rèn)證請(qǐng)求
RP使用OAuth2的Authorization-Code的方式來(lái)完成用戶身份認(rèn)證,所有的Token都是通過(guò)OP的Token EndPoint(OAuth2中定義)來(lái)發(fā)放的。構(gòu)建一個(gè)OIDC的Authentication Request需要提供如下的參數(shù):
- scope:必須。OIDC的請(qǐng)求必須包含值為“openid”的scope的參數(shù)。
- response_type:必選。同OAuth2。
- client_id:必選。同OAuth2。
- redirect_uri:必選。同OAuth2。
- state:推薦。同OAuth2。防止CSRF, XSRF。
示例如下:
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
4.2.2 基于Authorization Code的認(rèn)證請(qǐng)求的響應(yīng)
在OP接收到認(rèn)證請(qǐng)求之后,需要對(duì)請(qǐng)求參數(shù)做嚴(yán)格的驗(yàn)證,具體的規(guī)則參見(jiàn)http://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation,驗(yàn)證通過(guò)后引導(dǎo)EU進(jìn)行身份認(rèn)證并且同意授權(quán)。在這一切都完成后,會(huì)重定向到RP指定的回調(diào)地址(redirect_uri),并且把code和state參數(shù)傳遞過(guò)去。比如:
HTTP/1.1 302 Found
Location: https://client.example.org/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
4.2.3 獲取ID Token
RP使用上一步獲得的code來(lái)請(qǐng)求Token EndPoint,這一步桶OAuth2,就不再展開(kāi)細(xì)說(shuō)了。然后Token EndPoint會(huì)返回響應(yīng)的Token,其中除了OAuth2規(guī)定的部分?jǐn)?shù)據(jù)外,還會(huì)附加一個(gè)id_token的字段。id_token字段就是上面提到的ID Token。例如:
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"
}
其中看起來(lái)一堆亂碼的部分就是JWT格式的ID-Token。在RP拿到這些信息之后,需要對(duì)id_token以及access_token進(jìn)行驗(yàn)證(具體的規(guī)則參見(jiàn)http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation和http://openid.net/specs/openid-connect-core-1_0.html#ImplicitTokenValidation)。至此,可以說(shuō)用戶身份認(rèn)證就可以完成了,后續(xù)可以根據(jù)UserInfo EndPoint獲取更完整的信息。
4.2.4 安全令牌 ID-Token
上面提到過(guò)OIDC對(duì)OAuth2最主要的擴(kuò)展就是提供了ID-Token。下面我們就來(lái)看看ID-Token的主要構(gòu)成:
- iss = Issuer Identifier:必須。提供認(rèn)證信息者的唯一標(biāo)識(shí)。一般是Url的host+path部分;
- sub = Subject Identifier:必須。iss提供的EU的唯一標(biāo)識(shí);最長(zhǎng)為255個(gè)ASCII個(gè)字符;
- aud = Audience(s):必須。標(biāo)識(shí)ID-Token的受眾。必須包含OAuth2的client_id;
- exp = Expiration time:必須。ID-Token的過(guò)期時(shí)間;
- iat = Issued At Time:必須。JWT的構(gòu)建的時(shí)間。
- auth_time = AuthenticationTime:EU完成認(rèn)證的時(shí)間。如果RP發(fā)送認(rèn)證請(qǐng)求的時(shí)候攜帶max_age的參數(shù),則此Claim是必須的。
- nonce:RP發(fā)送請(qǐng)求的時(shí)候提供的隨機(jī)字符串,用來(lái)減緩重放攻擊,也可以來(lái)關(guān)聯(lián)ID-Token和RP本身的Session信息。
- acr = Authentication Context Class Reference:可選。表示一個(gè)認(rèn)證上下文引用值,可以用來(lái)標(biāo)識(shí)認(rèn)證上下文類。
- amr = Authentication Methods References:可選。表示一組認(rèn)證方法。
- azp = Authorized party:可選。結(jié)合aud使用。只有在被認(rèn)證的一方和受眾(aud)不一致時(shí)才使用此值,一般情況下很少使用。
{
"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必須使用JWT(JSON Web Token)進(jìn)行簽名和JWE(JSON Web Encryption)加密,從而提供認(rèn)證的完整性、不可否認(rèn)性以及可選的保密性。關(guān)于JWT的更多內(nèi)容,請(qǐng)參看JSON Web Token - 在Web應(yīng)用間安全地傳遞信息
4.2 默認(rèn)模式流程

默認(rèn)流程和OAuth中的類似,只不過(guò)也是添加了ID-Token的相關(guān)內(nèi)容。
這里需要說(shuō)明的是:OIDC的說(shuō)明文檔里很明確的說(shuō)明了用戶的相關(guān)信息都要使用JWT形式編碼。在JWT中,不應(yīng)該在載荷里面加入任何敏感的數(shù)據(jù)。如果傳輸?shù)氖怯脩舻腢ser ID。這個(gè)值實(shí)際上不是什么敏感內(nèi)容,一般情況下被知道也是安全的。
但是現(xiàn)在工業(yè)界已經(jīng)不推薦使用OAuth默認(rèn)模式,而推薦使用不帶client_Secret的授權(quán)碼模式。
4.3 混合模式
混合模式簡(jiǎn)而言之就是以上提到的兩種模式的混合,不過(guò)也有一些小的改變,就是允許直接向客戶端返回Access-Token。
業(yè)界普遍認(rèn)為,后端傳遞Token(比如服務(wù)器之間通信)要比前端(比如頁(yè)面之間)可靠,所以如果直接返回令牌的情況下會(huì)把令牌的過(guò)期時(shí)間設(shè)置較短,但是比較
UserInfo Endpoint
可能有的讀者發(fā)現(xiàn)了,ID-Token只有sub是和EU相關(guān)的,這在一般情況下是不夠的,必須還需要EU的用戶名,頭像等其他的資料,OIDC提供了一組公共的cliams,來(lái)提供更多用戶的信息,這就是——UserIndo EndPoin。
在RP得到Access Token后可以請(qǐng)求此資源,然后獲得一組EU相關(guān)的Claims,這些信息可以說(shuō)是ID-Token的擴(kuò)展,ID-Token中只需包含EU的唯一標(biāo)識(shí)sub即可(避免ID Token過(guò)于龐大和暴露用戶敏感信息),然后在通過(guò)此接口獲取完整的EU的信息。此資源必須部署在TLS之上,例如:
GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer SlAV32hkKG
成功之后響應(yīng)如下:
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": "janedoe@example.com",
"picture": "http://example.com/janedoe/me.jpg"
}
其中sub代表EU的唯一標(biāo)識(shí),這個(gè)claim是必須的,其他的都是可選的。
參考文獻(xiàn)
更多關(guān)于OIDC的內(nèi)容,請(qǐng)閱讀以下文章: