夜雨
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    1
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    <style> .red { color: red; } </style> # SSO 技術實踐: CAS整合JWT(Server篇) ###### tags: `CAS`, `JWT` ## 前言: 在研究CAS的時候撞得滿頭包,老樣子會記錄一些遇到的問題,也會稍微介紹一下CAS protocol整體流程以及和jwt流程的差異性。 對於Single Sign On為何,相信網路上已有諸多解釋,在此不多贅述。 但需要先有一個觀念是,User登入時會有兩種session,一種是SSO session,另一種則是在Client local site session。 因此即便從sso中登出,廢止了sso session,<span class="red">如果Server沒有通知底下全部的Client Service將這個User登出的話,就會發生已登入的Client Service,因為session沒有被廢除,所以依然還在登入狀態,但其他的Client Service卻會重新要求登入,導致狀態不一致</span>。 -------------------------------------------------- ## 使用版本及使用套件: * CAS Server v6.0: https://github.com/apereo/cas-overlay-template/tree/6.0 * CAS Client: https://github.com/cas-projects/cas-sample-java-webapp * Tomcat Server: CAS Server * 1、CAS Client * 2 CAS Protocol v3.0 * Gradle 6.5 Server使用套件(請加入在build.gradle): * [JWT as Service Ticket](https://apereo.github.io/cas/6.0.x/installation/Configure-ServiceTicket-JWT.html)[1] * [LDAP Authentication](https://apereo.github.io/cas/6.0.x/installation/LDAP-Authentication.html#ldap-authentication) [2] Client使用套件(請加入在pom.xml): * CAS Client相關設定傳送門: [SSO 技術實踐: CAS整合JWT(Client篇)](/klP2PIEDSsOBgXLyABAQpw) * [JOSE4j](https://mvnrepository.com/artifact/org.bitbucket.b_c/jose4j/0.7.0) :解析和驗證jwt字串 * [JSON](https://mvnrepository.com/artifact/org.json/json/20190722) * [OPENSAML J](https://mvnrepository.com/artifact/org.opensaml/opensaml/2.6.4) :接收Server發送的SLO request時需要解析saml字串 -------------------------------------------------- ## CAS相關名詞: Ticket Granting Tikcet(TGT): 用來證明使用者已經在CAS系統登入過,登入成功後系統會將TGT存在快取,並將id的值存入TGC,這樣之後只要比對id就能知道使用者是否登入。 Ticket Granting Cookie(TGC): 就是用來存TGT id的作用,登入後會將此物件返回給使用者,驗證時會攜帶TGC到Server確認是否登入。 Service Ticket(ST): 除了返回TGC以外,CAS還會為當前使用的服務(也就是當下的CAS Client)註冊,產生票據後連同TGC一起返回給使用者。 -------------------------------------------------- ## CAS流程介紹: 先附上CAS社群的使用指南: https://apereo.github.io/cas/6.0.x/protocol/CAS-Protocol.html#cas-protocol 從圖片中可以看到整個protocol的時序圖和整體流程,不過我自己畫了張流程圖,接著會以下圖來依序說明: ![](https://i.imgur.com/MSSW5Lz.png) 1. 使用者(browser)先訪問CAS Client(以下簡稱client),會先檢查此session是否在client local存在過。 2. 若尚未登入,此時會將使用者重新導向到CAS Server(以下簡稱Server)。 3. Server會檢查是否攜帶TGC,如果沒有就重新登入,有的話就簽發ST後返回給Client 4. 此時應該已經拿到ST了,驗證過ST之後,Client最後就會發放請求資源給使用者 -------------------------------------------------- ## JWT流程介紹: 一樣先附上社群文章: https://apereo.github.io/cas/6.6.x/installation/Configure-ServiceTicket-JWT.html jwt的流程也是基於CAS protocol的流程下去做調整,為何會選擇將jwt替換掉ST呢? 其實這兩者的功用是完全一樣的,差別只在於如果希望拿到ST後<span class="red">不用再回去跟Server驗證一次,而是在Client端的部分驗證jwt字串</span>,字串裡面會包含有ST的資訊以及過期時間等等資訊,只要驗證過後就馬上發放請求資源,<span class="red">進而減少Client和Server溝通的次數</span>。 ![](https://i.imgur.com/7ozvCgl.png) 因為流程很類似,只有最後不同,就不多加描述了~ -------------------------------------------------- ## CAS Server一些相關設定細節: 接下來這段都是實作中遇到的一些bug shooting和設定問題還有雜談,或許遇到問題時這邊能找到一些答案~ #### 雜談: * 從社群上可以看到使用了Overlay方式來deploy專案,因為他不希望你動到cas project的原始碼的關係,所以即便你抓了下來,也看不到任何的原始碼,會覺得怎麼專案就這麼空?? 他的概念是如果你有想要針對cas server做設定,或者想要覆蓋原先檔案的寫法的話,必須要在<span class="red">`project path\cas\src\main\resources`</span>這個路徑底下放上去你的設定檔案,例如針對cas server設定很重要的<span class="red">`application.properties`</span>。[4] * 有一點必須說一下,假設今天當你從git抓下來後,build完之後,他會產出一個cas.jar,只需要把這個jar deploy到tomcat,啟動後就ok了~ 但是這樣對開發來說時常需要去調整設定檔什麼之類的,每改一次就要build一次再重新deploy真的太煩人!!! 所以建議直接將專案導入到eclipse或個人習慣用的ide,同時把cas.jar解壓後,將`cas\WEB-INF\classes`這個資料夾設定成專案的source之一,這樣啟動後才會有cas project預設的一些html可以呈現。 ![](https://i.imgur.com/9kFHSbs.png) #### application.properties相關設定: 1. log設定: 有不少資訊是需要trace層級才能看見,也可另外定義log設定檔 ```xml ## # CAS Log4j Configuration # # logging.config=file:/etc/cas/log4j2.xml server.servlet.context-parameters.isLog4jAutoInitializationDisabled=true logging.level.org.apereo.cas=DEBUG ``` 2. CAS Server域名設定,這段不見得需要加,但有發生過不加的話,好像會吃預設的域名(test.sso.com什麼之類的),而不是自己設定的部分。 ```xml # CAS Server cas.server.name=https://sso.server.com:8443 cas.server.prefix=https://sso.server.com:8443/cas ``` 3. SSL設定: key-store-password是當初設定給keystore這個檔案的密碼,key-password是keystore裡面有記錄著一把私鑰,也可以為他設定密碼,如果當初產keystore沒有特地在把key加密的話,就不需要設定。 ```xml ## # CAS Web Application Embedded Server SSL Configuration # server.ssl.key-store=file:keystore的路徑\ssodemo.keystore server.ssl.key-store-password=123456 #server.ssl.key-password=123456 ``` 4. CAS認證: 官方會建議你不要使用本地認證的部分,而是搭配像是ldap或其他認證方式都有支援,但如果只是自己要開發測試,不想這麼麻煩,就依照第一個方式設定,這邊也額外補充使用了ad ldap的設定相關方式。 * 這邊<span class="red">強烈建議</span>去看社群[v5.0](https://apereo.github.io/cas/5.0.x/installation/Configuration-Properties.html#ldap-connection-pool)的設定屬性表比較齊全[6],當初看6.0的時候少了一些屬性,加上又將相關屬性散落在各個地方,真的不是很好查,在設定的時候真的曾經卡到懷疑人生.. ```xml ## # CAS Authentication Credentials # cas.authn.accept.users=test::123456 #cas.authn.accept.name=Static Credentials # LDAP Authentication Connection Setting # LDAP 的相關設定請參考cas 5.0版本的會比較齊全 #cas.authn.ldap[0].type=AD # basedn = ldap物件的基底位址,代表輸入帳號密碼認證時會從這個路徑開始找 #cas.authn.ldap[0].baseDn=cn=xxx,dc=xxx #cas.authn.ldap[0].subtreeSearch=true #cas.authn.ldap[0].searchFilter=cn={user} #cas.authn.ldap[0].enhanceWithEntryResolver=true #cas.authn.ldap[0].dnFormat=cn=%s,cn=xxx,dc=xxx #cas.authn.ldap[0].ldapUrl=ldap://ldap的ip #cas.authn.ldap[0].useSsl=false #cas.authn.ldap[0].useStartTls=false #cas.authn.ldap[0].connectTimeout=5000 # binddn = 在AD裡任何一位使用者的DN位址(為了一開始執行使用者身分認證時要進入ldap取得資料,較新的版本會稱為principalName) #cas.authn.ldap[0].bindDn=cn=xxx,dc=xxx #cas.authn.ldap[0].bindCredential=上面那組帳號的密碼 #cas.authn.ldap[0].providerClass=org.ldaptive.provider.unboundid.UnboundIDProvider #cas.authn.ldap[0].connectTimeout=PT10S # LDAP Authentication principal設定,想回傳想要的屬性可在此設定 #cas.authn.ldap[0].principalAttributeId=sAMAccountName #cas.authn.ldap[0].principalAttributeList=givenName,cn,mail #cas.authn.ldap[0].collectDnAttribute=true #cas.authn.ldap[0].allowMultiplePrincipalAttributeValues=true #cas.authn.ldap[0].allowMissingPrincipalAttributeValue=true ``` 5. Service Registry: <span class="red">這部分非常重要</span>,算是一定要設定部分,因為要讓Server知道到底有哪些client訪問是可以被註冊的,這邊採取用json設定方式(社群網站有其他方式,請自行參閱)。[7] ```xml # Service Registry cas.serviceRegistry.watcherEnabled=true cas.serviceRegistry.schedule.repeatInterval=120000 cas.serviceRegistry.schedule.startDelay=15000 # Auto-initialize the registry from default JSON service definitions cas.serviceRegistry.initFromJson=true ``` * 這邊還要搭配json檔案的配置,請在`project path\cas\src\main\resources`底下,建立一個資料夾`services`,並且建立json檔案,檔名有規定格式,需命名為:name-id.json*。 像下方name: client1_server、id 1,所以檔名就是: client1_server-1.json。 ```json { "@class" : "org.apereo.cas.services.RegexRegisteredService", "serviceId" : "^(https|imaps)://自己設定的client域名", "name" : "client1_server", "id" : 1, "evaluationOrder" : 1 } ``` 6. JWT Ticket設定: 要使用jwt as service ticket[5],務必要在build.gradle裡面加入套件,並且除了在application.properties設定後,service部分也需要設定。 * 如果感覺一直吃不到設定檔的感覺,可以去把classes的資料夾,把他預設的application.properties檔案給砍了,只留下自己寫的即可。 * 如果不知道jwt的signing key和encrpytion key要設定成什麼,<span class="red">可以先在不設定的情況下啟動一次Server,正常順利的話看看console,系統會自動產出一組給你,請把它複製起來貼在json裡面。</span> ```xml # JWT Tickets cas.authn.token.crypto.encryptionEnabled=true cas.authn.token.crypto.signingEnabled=true ``` * 請把這段json code加在service.json裡面 ```json "properties" : { "@class" : "java.util.HashMap", "jwtAsServiceTicket" : { "@class" : "org.apereo.cas.services.DefaultRegisteredServiceProperty", "values" : [ "java.util.HashSet", [ "true" ] ] }, "jwtAsServiceTicketSigningKey" : { "@class" : "org.apereo.cas.services.DefaultRegisteredServiceProperty", "values" : [ "java.util.HashSet", [ "7gInlM_CH3WTNIiatPWHr_...." ] ] }, "jwtAsServiceTicketEncryptionKey" : { "@class" : "org.apereo.cas.services.DefaultRegisteredServiceProperty", "values" : [ "java.util.HashSet", [ "f5E4tGS9v6IXEcm57ix...." ] ] } } ``` 7. Single Logout(SLO)設定: <span class="red">這邊一樣非常重要!</span> 如果這邊沒設定的話,即便社群有說SLO預設是啟動的,但沒設定要發送到client的哪個位子,基本上client也收不到,就做不了事情...[8][9] * 這個設定預設是false,主要是設定如果網址後面帶service=xxxx,當server做完事情後,會導回client中你指定的url。(請參閱[CAS Protocol 2.3.1](https://apereo.github.io/cas/6.0.x/protocol/CAS-Protocol-Specification.html#231-parameters)) ```xml # cas logout setting cas.logout.followServiceRedirects=true cas.slo.asynchronous=true ``` * 一樣將下列這段json code加進service.json中,預設是back_channel,這種方式會直接讓server背地裡直接傳request給client,如果是front_channel,要登出前就會跳出視窗讓使用者確認。 ```json "logoutType": "BACK_CHANNEL", "logoutUrl": "看你想將它導到client的哪個頁面的網址" ``` ## Reference: [1] [JWT Service Ticket](https://apereo.github.io/cas/6.6.x/installation/Configure-ServiceTicket-JWT.html) [2] [LDAP Authentication](https://apereo.github.io/cas/6.6.x/installation/LDAP-Authentication.html#ldap-authentication) [3] [CAS Protocol](https://apereo.github.io/cas/6.6.x/protocol/CAS-Protocol.html#cas-protocol) [4] [CAS Overlay](https://apereo.github.io/cas/6.6.x/installation/WAR-Overlay-Installation.html#cas-war-overlays) [5] [CAS Properties: JWT Ticket](https://apereo.github.io/cas/6.6.x/configuration/Configuration-Properties.html#jwt-tickets) [6] [CAS Properties: LDAP Connection Pool v5.0](https://apereo.github.io/cas/5.0.x/installation/Configuration-Properties.html#ldap-connection-pool) [7] [CAS Properties: Service Registry](https://apereo.github.io/cas/6.6.x/configuration/Configuration-Properties.html#service-registry) [8] [CAS Properties: Single Logout](https://apereo.github.io/cas/6.6.x/configuration/Configuration-Properties.html#single-logout) [9] [Single Logout(SLO)](https://apereo.github.io/cas/6.6.x/installation/Logout-Single-Signout.html) > [name=夜雨] > 由於一開始第一次接觸什麼叫Overlay的關係,一開始抓git抓CAS Server專案包下來的時候選了master版本,結果只看到抓下來一大堆各式各樣的xxx server,看得頭昏眼花又搞不太懂,很不像傳統一個網頁project的樣子...重點是build又要超久!! 後來選了6.0版後,才終於看起來有比較像平常看到的那種專案結構了...

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully