<style> .red { color: red; } </style> # cas-sample-java-webapp tutorial ###### tags: `CAS` ## 前置作業: * 建立cas client專案以前,需要搭配cas server建立,目前cas server是透過tomcat配置,詳細配置可參考: [架設CAS Server and Client Tomcat SSL 設定](/rqPAuRsQRxm87BnavdxVeQ) * 本篇會使用到的範例憑證檔案,含: casclient1.keystore、casclient1.crt * 本專案配置在glassfish server上,一樣需要將上述兩個檔案匯入到glassfish server內部,詳細配置可參考: [Glassfish ssl及自建domain設定](/nx5oFMx0Rq66YJWM1XM0Mw) * 本專案jar檔管理有套用到maven,所有使用到的jar在`cas-sample-java-webapp1\src\main\java\libs`裡面皆有備份,可透過本地方式配置dependencies * 本專案僅只作為範例,實作時需自行依據所需去撰寫相關了jsf的xhtml以及相關的bean.java等等,以及需要自行設定filter的流向。 ## 主要介紹: #### 1. 先設定web.xml,包含cas login、filter控制request流向等等。 * 設定single sign out必要的listener,僅需依照預設保留即可。 ```xml= <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> ``` * single sign out filter: 設定cas server的位子,可替換成server實際ip位子,配置的方式透過forward才會進入,後續會解釋為什麼。 此filter作用是為導到cas server去檢查是否登入狀態,基本上會設定在login的位子 ```xml= <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://sso.server.com:8443/cas</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/login.xhtml</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping> ``` * cas authentication filter: casServerLoginUrl代表cas login的位子,因為cas是在server端做登入管理,因此必須設定cas server位子。 serverName代表client初始位子。 此filter是用來導入到cas server login page的地方,做登入驗證的部分 ```xml= <filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://sso.server.com:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>https://sso.client1.com:18443/cas-sample-java-webapp1/index.xhtml</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/login.xhtml</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping> ``` * CAS HttpServletRequest Wrapper Filter: 非必要配置,單純可透過HttpServletRequest的getRemoteUser()方法獲得SSO登入使用者的登入名。 * loginFilter: 所有request都必須流經此filter,先行判斷是否包含jwt token、是否已登入等等 * validatorFilter: 必定要有的filter,負責檢查所有request的jwt token是否合格、簽發時間是否過時等等 * Faces Servlet: jsf套件需要的相關配置。 #### 2. 撰寫loginFilter.java ```java= if(httpRequest.getServletPath().contains("css") || httpRequest.getQueryString() != null) { chain.doFilter(request, response); } else if(httpRequest.getServletPath().equals("/logout.xhtml")) { chain.doFilter(request, response); } else if(httpRequest.getServletPath().equals("/login.xhtml")) { httpRequest.getRequestDispatcher("/login.xhtml").forward(request, response); } else if(httpRequest.getSession().getAttribute("jwt") == null) { httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.xhtml"); } else { chain.doFilter(request, response); } ``` 負責管控所有request的第一層控制,範例中會有`httpRequest.getServletPath().contains("css")`是因為當經過cas server login完畢之後,流經faces servlet之後,還會再發一個關於jsf css的request過來,請直接pass過去即可。 其餘就是一些jwt檢查的判斷,若已經在登入狀態就直接流到最後即可。 #### 3. 撰寫validateFilter.java ```java= if (httpRequest.getServletPath().contains("css")) { chain.doFilter(request, response); } else if (jwtValidate.jwtvalid(httpRequest) && userlist.contains(httpRequest.getSession().getId())) { if (httpRequest.getServletPath().equals("/login.xhtml")) { httpResponse.sendRedirect(httpRequest.getContextPath() + "/index.xhtml"); } else { chain.doFilter(request, response); } } else { httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.xhtml"); } ``` 此filter會實作jwt檢查的部分,主要由`jwtValidate.jwtvalid(request)`來執行,JwtValidate使用相關可參考[SSO 技術實踐: JwtValidate.jar使用相關說明](/Z0FxKELtR3io9loq5pUqzQ),有個重點是這邊的Request可接收jwt的狀況分兩種: 一種是在session裡面塞"jwt"的屬性,或者是cas server回傳網址會含有ticket=xxx(這就是jwt)才有辦法驗證。 #### 4. 撰寫logoutFilter.java ```java= String body = request.getReader().lines().collect(Collectors.joining()); System.out.println("body: " + URLDecoder.decode(body, "UTF-8")); String samlString = ""; if (body.contains("logoutRequest=")) { samlString = URLDecoder.decode(body, "UTF-8").split("logoutRequest=")[1]; } if (userlist.contains(httpRequest.getSession().getId())) { // local logout HttpSession session = userlist.getSessionById(httpRequest.getSession().getId()); userlist.removeUserById(httpRequest.getSession().getId()); session.invalidate(); } else if (samlString != null) { // cas server single sign out HashMap<String, String> attributes = samlUtils.getAttributes(samlString); HttpSession session = userlist.getSessionByST(attributes.get("SessionIndex")); if (session != null) { userlist.removeUserByST(attributes.get("SessionIndex")); session.invalidate(); } } httpResponse.sendRedirect("https://sso.server.com:8443/cas/logout?service=https://sso.client1.com:18443/cas-sample-java-webapp1/login.xhtml"); ``` 負責登出的部分,主要分為兩種,一種是cas client端local登出,本身session直接作廢;另一種則是當有其他的client登出後,透過cas server發送logout request過來通知自己本身也要登出時的狀況。 由於透過logout request的部分,會需要解析saml的部分,這部分也已經打包在JwtValidate.jar裡面了,可直接import SamlUtils來使用,發過來字串中會有`SessionIndex`的屬性,可取得是哪個session id需要作廢。 JwtValidate.jar本身有包含一些簡單的bean用來管理user和user清單,這部分可考慮是否自行撰寫或擴增,並覆蓋到jar檔裡面 #### 5. 剩下就是撰寫相關的xhtml以及bean了,在此不多贅述。
×
Sign in
Email
Password
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