在wildfly中使用SAML協議連接keycloak 簡介 OpenID Connect和SAML SAML的工作流程 在keycloak中使用SAML 準備wildfy和應用程序

簡介

我們知道SSO的兩個常用的協議分別是SAML和OpenID Connect,我們在前一篇文章已經講過了怎麼在wildfly中使用OpenID Connect連接keycloak,今天我們會繼續講解怎麼使用SAML協議連接keycloak。

OpenID Connect和SAML

OpenID Connect簡稱OIDC,是一個基於OAuth2協議的認證框架。爲什麼要基於OAuth2框架呢?因爲OAuth2協議只是一個授權協議,它是不完備的,並且也沒有指明具體的實現方式。所以這一切都由 OpenID Connect來填補。

OpenID Connect同時包含了認證和授權,並且使用Json Web Token(JWT)來進行消息的傳遞。

一般來說OpenID Connect有兩種使用場景,第一種場景是某個應用程序請求keycloak來幫它認證一個用戶。該應用程序並不存儲這個用戶的認證信息。所以用戶需要在keycloak中進行登錄,登錄成功之後keycloak會返回應用程序一個identity token 和 access token。

identity token主要包含用戶的基本信息,包括用戶名,郵箱和一些其他的信息。access token主要包含的是用戶的訪問權限信息,比如說用戶的角色等。應用程序可以通過使用access token來判斷用戶到底可以訪問應用程序的哪些資源。

還有一種場景就是client想去訪問遠程服務的資源,這種情況下client可以先從keycloak中獲取到access token,然後使用這個access token去遠程服務中請求資源。遠程服務器收到了這個請求之後,會去驗證這個access token,然後根據token去獲取相應的信息。

SAML 2.0是基於XML的認證協議,它是在OIDC之前產生的,所以會比OIDC成熟,但是相應的也會比OIDC複雜。

SAML使用XML在應用程序和認證服務器中交換數據,同樣的SAML也有兩種使用場景。

第一種場景是某個應用程序請求keycloak來幫它認證一個用戶。該應用程序並不存儲這個用戶的認證信息。所以用戶需要在keycloak中進行登錄,登錄成功之後keycloak會返回應用程序一個XML文件,這個文件裏面包含了一個叫做SAML assertion的東西,裏面存的是用戶的信息,同時這個XML文件中還包含了用戶的權限信息,應用程序可以根據這個信息來對程序進行訪問工作。

還有一種場景就是client想去訪問遠程服務的資源,這種情況下client可以先從keycloak中獲取到SAML assertion,然後使用這個SAML assertion去遠程服務中請求資源。

所以總結起來,一般情況下是推薦是用OIDC的,因爲它比較簡單和多平臺支持性更強。使用SAML的場景主要考慮的是SAML的成熟性,或者說公司中已經在使用了SAML了。

SAML的工作流程

在SAML協議中定義了三個角色,分別是principal:代表主體通常表示人類用戶。identity provider (IdP)身份提供者和service provider (SP)服務提供者。

IdP的作用就是進行身份認證,並且將用戶的認證信息和授權信息傳遞給服務提供者。

SP的作用就是進行用戶認證信息的驗證,並且授權用戶訪問指定的資源信息。

根據請求方式有redirect和post的不同,使用SAML來進行SSO認證有通常有三種方式,我們這裏介紹最簡單的一種叫做SP redirect request; IdP POST response:

上圖中User Agent就是web瀏覽器,我們看一下如果用戶想請求Service Provider的資源的時候,SAML協議是怎麼處理的。

  1. 用戶通過User Agent請求Service Provider,比如:
http://sp.flydean.com/myresource

SP將會對該資源進行相應的安全檢查,如果發現已經有一個有效的安全上下文的話,SP將會跳過2-7步,直接進入第8步。

  1. 如果在第一步的時候,SP並沒有找到相應的有效安全上下文的話,則會生成對應的SAMLRequest,並將User Agent重定向到IdP:
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來進行簽名。

  1. User agent將會發送一個get請求到IdP的SSO server :
GET /SAML2/SSO/Redirect?SAMLRequest=request&RelayState=token HTTP/1.1
Host: idp.flydean.com

IdP收到這個AuthnRequest請求之後,將會進行安全驗證,如果是合法的AuthnRequest,那麼將會展示登錄界面。

  1. 用戶可以輸入用戶名密碼進行登錄。登錄成功之後,IdP將會返回一個XHTML form:
  <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信息。

  1. user agent 收到XHTML form之後將會提交該form給SP。

  2. SP中的assertion consumer service將會處理這個請求,創建相關的安全上下文,並將user agent重定向到要訪問的資源頁面。

  3. user agent再次請求SP資源。

  4. 因爲安全上下文已經創建完畢,SP可以直接返回相應的資源,不用再次到IdP進行認證。

我們可以看到上面的所有的信息交換都是由前端瀏覽器來完成的,在SP和IdP之間不存在直接的通信。

這種全部由前端來完成信息交換的方式好處就是協議流非常簡單,所有的消息都是簡單的GET或者POST請求。

如果爲了提高安全性,也可以使用引用消息。也就是說IdP返回的不是直接的SAML assertion,而是一個SAML assertion的引用。SP收到這個引用之後,可以從後臺再去查詢真實的SAML assertion,從而提高了安全性。

在keycloak中使用SAML

接下來,我們看下怎麼在keycloak中配置使用SAML協議。

我們通過./standalone.sh -Djboss.socket.binding.port-offset=100啓動keycloak服務器。訪問 http://localhost:8180/auth/admin 可以進入到admin console界面。

注意,這裏爲了和本地應用程序的默認端口8080區別,我們添加了一個-Djboss.socket.binding.port-offset=100參數,讓keycloak的端口從8080變成了8180。

輸入我們創建的admin用戶名和密碼,就可以登錄到keycloak的admin界面。

這裏需要爲SAML應用創建一個新的client。

點擊clients-> create 輸入Client ID和Client Protocol: saml,點擊save即可創建新的client。

成功創建client之後,假設我們要部署的應用程序名叫做app-profile-saml,則需要添加下面的信息:

Valid Redirect URIs: http://localhost:8080/app-profile-saml/*

Base URL: http://localhost:8080/app-profile-saml/

Master SAML Processing URL: http://localhost:8080/app-profile-saml/saml

Force Name ID Format: ON

點保存即可。

接下來我們需要點擊mappers,創建一些用戶信息和token claims的映射信息,從而能夠在saml的請求中包含這些用戶信息。

爲了簡單起見,我們選擇默認的Protocol Mapper:

最後一步,我們需要配置adapter。

點擊installation,選擇Keycloak SAML Adapter keycloak-saml.xml, 點擊下載。

將下載下來的keycloak-saml.xml進行修改:

將 logoutPage="SPECIFY YOUR LOGOUT PAGE!" 修改爲 /index.jsp

將 entityID="saml-test" 中的entityID修改爲我們設置的entityID

將keycloak-saml.xml拷貝到我們應用程序的config/目錄下。這裏我們使用官方的應用程序,大家可以在 https://github.com/keycloak/keycloak-quickstarts/tree/latest/app-profile-saml-jee-jsp 進行下載。

在下一節,我們將會詳細講解這個應用程序的功能和結構。

準備wildfy和應用程序

我們從wildfly官網下載wildfly應用程序之後,還需要到keycloak中下載wildfly Client Adapters。

這裏因爲我們使用的是SAML,所以需要下載 keycloak-saml-wildfly-adapter-dist-11.0.2.zip。

下載完畢之後,將其拷貝到wildfly根目錄,解壓即可。

解壓adapter,解壓之後,進入wildfly/bin目錄,運行:

./jboss-cli.sh --file=adapter-elytron-install-offline.cli

即可安裝完畢。

安裝完畢之後,記得啓動wildfly應用程序。

接下來可以編譯我們的應用程序了:

cd app-profile-saml-jee-jsp 
mvn clean wildfly:deploy 

即可將我們的應用程序部署到wildfly中。

先看下應用的運行情況,訪問 http://localhost:8080/app-profile-saml/

點擊login,可以看到跳轉到了keycloak的登錄頁面:

輸入用戶命名密碼之後就會跳轉到profile.jsp頁面,從而展示用戶的profile信息。

簡單講解一下應用程序的工作流程。

應用程序主要有兩個頁面,一個是index,一個是profile。在index頁面會去檢測用戶是否登錄。如果未登錄,可以點擊登錄按鈕,跳轉到登錄頁面。

輸入用戶名和密碼進行校驗之後,keycloak會返回一個SAMLResponse給應用程序,應用程序通過assertion consumer service將會處理這個請求,創建相關的安全上下文,並將user agent重定向到要訪問的資源頁面。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/keycloak-saml-wildfly/

本文來源:flydean的博客

歡迎關注我的公衆號:「程序那些事」最通俗的解讀,最深刻的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章