SAML的全稱是Security Assertion Markup Language, 是由OASIS制定的一套基於XML格式的開放標準,用在身份提供者(IdP)和服務提供者 (SP)之間交換身份驗證和受權數據。前端
SAML的一個很是重要的應用就是基於Web的單點登陸(SSO)。java
接下來咱們一塊兒來看看SAML是怎麼工做的。web
在SAML協議中定義了三個角色,分別是principal:表明主體一般表示人類用戶。identity provider (IdP)身份提供者和service provider (SP)服務提供者。shell
IdP的做用就是進行身份認證,而且將用戶的認證信息和受權信息傳遞給服務提供者。瀏覽器
SP的做用就是進行用戶認證信息的驗證,而且受權用戶訪問指定的資源信息。安全
爲何要使用SAML呢?服務器
第一能夠提高用戶體驗,若是系統使用SAML,那麼能夠在登陸一次的狀況下,訪問多個不一樣的系統服務。這實際上也是SSO的優點,用戶不須要分別記住多個系統的用戶名和密碼,只用一個就夠了。ide
第二能夠提高系統的安全性,使用SAML,咱們只須要向IdP提供用戶名密碼便可,post
第三用戶的認證信息不須要保存在全部的資源服務器上面,只須要在在IdP中存儲一份就夠了。編碼
接下來,咱們經過一個用SAML進行SSO認證的流程圖,來分析一下SAML是怎麼工做的。
根據請求方式有redirect和post的不一樣,使用SAML來進行SSO認證有一般有三種方式,咱們一一道來。
上圖中User Agent就是web瀏覽器,咱們看一下若是用戶想請求Service Provider的資源的時候,SAML協議是怎麼處理的。
http://sp.flydean.com/myresource
SP將會對該資源進行相應的安全檢查,若是發現已經有一個有效的安全上下文的話,SP將會跳過2-7步,直接進入第8步。
302 Redirect Location: https://idp.flydean.com/SAML2/SSO/Redirect?SAMLRequest=request&RelayState=token
RelayState是SP維護的一個狀態信息,主要用來防止CSRF攻擊。
其中這個SAMLRequest是用Base64編碼的samlp:AuthnRequest,下面是一個samlp:AuthnRequest的例子:
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="aaf23196-1773-2113-474a-fe114412ab72" Version="2.0" IssueInstant="2020-09-05T09:21:59Z" AssertionConsumerServiceIndex="0" AttributeConsumingServiceIndex="0"> <saml:Issuer>https://sp.flydean.com/SAML2</saml:Issuer> <samlp:NameIDPolicy AllowCreate="true" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/> </samlp:AuthnRequest>
爲了安全起見,SAMLRequest還可使用SP提供的簽名key來進行簽名。
GET /SAML2/SSO/Redirect?SAMLRequest=request&RelayState=token HTTP/1.1 Host: idp.flydean.com
IdP收到這個AuthnRequest請求以後,將會進行安全驗證,若是是合法的AuthnRequest,那麼將會展現登陸界面。
<form method="post" action="https://sp.flydean.com/SAML2/SSO/POST" ...> <input type="hidden" name="SAMLResponse" value="response" /> <input type="hidden" name="RelayState" value="token" /> ... <input type="submit" value="Submit" /> </form>
這個form中包含了SAMLResponse信息,SAMLResponse中包含了用戶相關的信息。
一樣的SAMLResponse也是使用Base64進行編碼過的samlp:Response。
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="identifier_2" InResponseTo="identifier_1" Version="2.0" IssueInstant="2020-09-05T09:22:05Z" Destination="https://sp.flydean.com/SAML2/SSO/POST"> <saml:Issuer>https://idp.flydean.com/SAML2</saml:Issuer> <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> </samlp:Status> <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="identifier_3" Version="2.0" IssueInstant="2020-09-05T09:22:05Z"> <saml:Issuer>https://idp.flydean.com/SAML2</saml:Issuer> <!-- a POSTed assertion MUST be signed --> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</ds:Signature> <saml:Subject> <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"> 3f7b3dcf-1674-4ecd-92c8-1544f346baf8 </saml:NameID> <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <saml:SubjectConfirmationData InResponseTo="identifier_1" Recipient="https://sp.flydean.com/SAML2/SSO/POST" NotOnOrAfter="2020-09-05T09:27:05Z"/> </saml:SubjectConfirmation> </saml:Subject> <saml:Conditions NotBefore="2020-09-05T09:17:05Z" NotOnOrAfter="2020-09-05T09:27:05Z"> <saml:AudienceRestriction> <saml:Audience>https://sp.flydean.com/SAML2</saml:Audience> </saml:AudienceRestriction> </saml:Conditions> <saml:AuthnStatement AuthnInstant="2020-09-05T09:22:00Z" SessionIndex="identifier_3"> <saml:AuthnContext> <saml:AuthnContextClassRef> urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport </saml:AuthnContextClassRef> </saml:AuthnContext> </saml:AuthnStatement> </saml:Assertion> </samlp:Response>
咱們能夠看到samlp:Response中包含有saml:Assertion信息。
user agent 收到XHTML form以後將會提交該form給SP。
SP中的assertion consumer service將會處理這個請求,建立相關的安全上下文,並將user agent重定向到要訪問的資源頁面。
user agent再次請求SP資源。
由於安全上下文已經建立完畢,SP能夠直接返回相應的資源,不用再次到IdP進行認證。
咱們能夠看到上面的全部的信息交換都是由前端瀏覽器來完成的,在SP和IdP之間不存在直接的通訊。
這種所有由前端來完成信息交換的方式好處就是協議流很是簡單,全部的消息都是簡單的GET或者POST請求。
若是爲了提升安全性,也可使用引用消息。也就是說IdP返回的不是直接的SAML assertion,而是一個SAML assertion的引用。SP收到這個引用以後,能夠從後臺再去查詢真實的SAML assertion,從而提升了安全性。
剛剛講的是SP redirect Request,這裏咱們看一下SP POST request是怎麼作的:
和第一種方式的不一樣之處在於第二步和第三步。
第二步:SP再也不進行redirect了,而是返回一個XHTML form給User agent:
<form method="post" action="https://idp.flydean.com/SAML2/SSO/POST" ...> <input type="hidden" name="SAMLRequest" value="request" /> <input type="hidden" name="RelayState" value="token" /> ... <input type="submit" value="Submit" /> </form>
第三步:拿到第二步的XHTML form以後,User agent將該form post到IdP SSO server。
從第四步開始就和第一種方式是同樣的了。
第三種方式,SP和IdP都用的是redirect,可是redirect的內容都是artifact。
以前咱們講了SAML message能夠以值的方式也能夠以引用的方式來進行傳遞。
而這種以引用的傳遞方式就是artifact。
收到artifact的receiver會發送一個samlp:ArtifactResolve 給issuer,從而得到真正的message。
下面是一個向IdP請求message的例子:
<samlp:ArtifactResolve xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_cce4ee769ed970b501d680f697989d14" Version="2.0" IssueInstant="2020-09-05T09:21:58Z"> <saml:Issuer>https://idp.flydean.com/SAML2</saml:Issuer> <!-- an ArtifactResolve message SHOULD be signed --> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</ds:Signature> <samlp:Artifact>AAQAAMh48/1oXIM+sDo7Dh2qMp1HM4IF5DaRNmDj6RdUmllwn9jJHyEgIi8=</samlp:Artifact> </samlp:ArtifactResolve>
相應的server會返回一個包含samlp:AuthnRequest的samlp:ArtifactResponse:
<samlp:ArtifactResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_d84a49e5958803dedcff4c984c2b0d95" InResponseTo="_cce4ee769ed970b501d680f697989d14" Version="2.0" IssueInstant="2020-09-05T09:21:59Z"> <!-- an ArtifactResponse message SHOULD be signed --> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</ds:Signature> <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> </samlp:Status> <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_306f8ec5b618f361c70b6ffb1480eade" Version="2.0" IssueInstant="2020-09-05T09:21:59Z" Destination="https://idp.flydean.com/SAML2/SSO/Artifact" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" AssertionConsumerServiceURL="https://sp.flydean.com/SAML2/SSO/Artifact"> <saml:Issuer>https://sp.flydean.com/SAML2</saml:Issuer> <samlp:NameIDPolicy AllowCreate="false" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/> </samlp:AuthnRequest> </samlp:ArtifactResponse>
看下第三種方式的流程圖:
能夠看到這種方式和前面兩種方式的區別就是多了一個請求真實message的步驟。
以第三,四,五步爲例:
第三步user agent請求IdP的SSO server:
https://idp.example.org/SAML2/SSO/Artifact?SAMLart=artifact_1&RelayState=token
注意這裏請求的參數變成了SAMLart。
第四步,IdP須要發送一個samlp:ArtifactResolve到SP來請求真正的samlp:AuthnRequest。
第五步,SP返回一個samlp:ArtifactResponse 包含samlp:AuthnRequest。
SAML協議和它的基本用法就是上面這樣。下面的文章咱們會舉一個具體的例子,來說解如何應用SAML協議。
本文做者:flydean程序那些事
本文連接:http://www.flydean.com/saml-startup/
本文來源:flydean的博客
歡迎關注個人公衆號:「程序那些事」最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!