天天看點

【SSO-CAS】SSO之CAS單點登入執行個體示範

内部邀請碼:C8E245J (不寫邀請碼,沒有現金送)

國内私募機構九鼎控股打造,九鼎投資是在全國股份轉讓系統挂牌的公衆公司,股票代碼為430719,為“中國PE第一股”,市值超1000億元。 

------------------------------------------------------------------------------------------------------------------------------------------------------------------

原文位址:http://www.micmiu.com/enterprise-app/sso/sso-cas-sample/

本文目錄:

一、概述

二、示範環境

三、JDK安裝配置

四、安全證書配置

五、部署CAS-Server相關的Tomcat

六、部署CAS-Client相關的Tomcat

七、 測試驗證SSO

此文的目的就是為了幫助初步接觸SSO和CAS 的人員提供一個入門指南,一步一步示範如何實作基于CAS的單點登入。

本文示範過程在同一個機器上的(也可以在三台實體機器或者三個的虛拟機上),環境如下:

windows7 64位,主機名稱:michael-pc

JDK 1.6.0_18

Tomcat 6.0.29

CAS-server-3.4.11、CAS-client-3.2.1

根據示範需求,用修改hosts 檔案的方法添加域名最簡單友善(這個非常重要),在檔案 C:\Windows\System32\drivers\etc\hosts 檔案中添加三條

demo.micmiu.com  =>> 對應部署cas server的tomcat,這個虛拟域名還用于證書生成

app1.micmiu.com  =>>  對應部署app1 的tomcat

app2.micmiu.com   =>> 對應部署app2 的tomcat

這個詳細過程就不在描述,如果是免安裝版的,確定環境變量配置正确。

本機環境變量:JAVA_HOME=D:\jdk,如果看到以下資訊則表示安裝成功:

【SSO-CAS】SSO之CAS單點登入執行個體示範

4.1. 生成證書:

<code>1</code>

<code>keytool -genkey -</code><code>alias</code> <code>ssodemo -keyalg RSA -keysize 1024 -keypass michaelpwd -validity 365 -keystore g:\sso\ssodemo.keystore -storepass michaelpwd</code>

【SSO-CAS】SSO之CAS單點登入執行個體示範

ps:

截圖中需要輸入的姓名和上面hosts檔案中配置的一緻;

keypass 和 storepass 兩個密碼要一緻,否則下面tomcat 配置https 通路失敗;

4.2.導出證書:

<code>keytool -</code><code>export</code> <code>-</code><code>alias</code> <code>ssodemo -keystore g:\sso\ssodemo.keystore -</code><code>file</code><code>g:\sso\ssodemo.crt -storepass michaelpwd</code>

【SSO-CAS】SSO之CAS單點登入執行個體示範

4.3.用戶端導入證書:

<code>keytool -</code><code>import</code> <code>-keystore %JAVA_HOME%\jre\lib\security\cacerts -</code><code>file</code><code>g:\sso\ssodemo.crt -</code><code>alias</code> <code>ssodemo</code>

【SSO-CAS】SSO之CAS單點登入執行個體示範

ps:該指令中輸入的密碼和上面輸入的不是同一個密碼;如果是多台機器示範,需要在每一台用戶端導入該證書。

5.1. 配置HTTPS

解壓apache-tomcat-6.0.29.tar.gz并重命名後的路徑為 G:\sso\tomcat-cas,在檔案 conf/server.xml檔案找到:

<code>&lt;!--</code>

<code>2</code>

<code>    </code><code>&lt;Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"</code>

<code>3</code>

<code>               </code><code>maxThreads="150" scheme="https" secure="true"</code>

<code>4</code>

<code>               </code><code>clientAuth="false" sslProtocol="TLS" /&gt;</code>

<code>5</code>

<code>    </code><code>--&gt;</code>

修改成如下:

<code>&lt;Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"</code>

<code>               </code><code>keystoreFile="g:/sso/ssodemo.keystore" keystorePass="michaelpwd"</code>

<code>               </code><code>clientAuth="false" sslProtocol="TLS" URIEncoding="UTF-8"</code>

參數說明:

keystoreFile 就是4.1中建立證書的路徑

keystorePass 就是4.1中建立證書的密碼

5.2. 驗證HTTPS配置

其他按照預設配置不作修改,輕按兩下%TOMCAT_HOME%\bin\startup.bat 啟動tomcat-cas 驗證https通路配置:

【SSO-CAS】SSO之CAS單點登入執行個體示範
【SSO-CAS】SSO之CAS單點登入執行個體示範

如果看到上述界面表示https 通路配置成功。

5.3 部署CAS-Server

CAS-Server 下載下傳位址:http://www.jasig.org/cas/download

本文以cas-server-3.4.11-release.zip 為例,解壓提取cas-server-3.4.11/modules/cas-server-webapp-3.4.11.war檔案,把改檔案copy到 G:\sso\tomcat-cas\webapps\ 目下,并重命名為:cas.war.

啟動tomcat-cas,在浏覽器位址欄輸入:https://demo.micmiu.com:8443/cas/login ,回車

【SSO-CAS】SSO之CAS單點登入執行個體示範

CAS-server的預設驗證規則:隻要使用者名和密碼相同就認證通過(僅僅用于測試,生成環境需要根據實際情況修改),輸入admin/admin 點選登入,就可以看到登入成功的頁面:

【SSO-CAS】SSO之CAS單點登入執行個體示範

看到上述頁面表示CAS-Server已經部署成功。

6.1Cas-Client 下載下傳

以cas-client-3.2.1-release.zip 為例,解壓提取cas-client-3.2.1/modules/cas-client-core-3.2.1.jar

借以tomcat預設自帶的 webapps\examples 作為示範的簡單web項目

6.2 安裝配置 tomcat-app1

解壓apache-tomcat-6.0.29.tar.gz并重命名後的路徑為 G:\sso\tomcat-app1,修改tomcat的啟動端口,在檔案conf/server.xml檔案找到如下内容:

<code>&lt;</code><code>Connector</code> <code>port</code><code>=</code><code>"8080"</code> <code>protocol</code><code>=</code><code>"HTTP/1.1"</code>

<code>               </code><code>connectionTimeout</code><code>=</code><code>"20000"</code>

<code>               </code><code>redirectPort</code><code>=</code><code>"8443"</code> <code>/&gt;</code>

<code>&lt;</code><code>Connector</code> <code>port</code><code>=</code><code>"8009"</code> <code>protocol</code><code>=</code><code>"AJP/1.3"</code> <code>redirectPort</code><code>=</code><code>"8443"</code> <code>/&gt;</code>

<code>&lt;</code><code>Connector</code> <code>port</code><code>=</code><code>"18080"</code> <code>protocol</code><code>=</code><code>"HTTP/1.1"</code>

<code>               </code><code>redirectPort</code><code>=</code><code>"18443"</code> <code>/&gt;</code>

<code>&lt;</code><code>Connector</code> <code>port</code><code>=</code><code>"18009"</code> <code>protocol</code><code>=</code><code>"AJP/1.3"</code> <code>redirectPort</code><code>=</code><code>"18443"</code> <code>/&gt;</code>

啟動tomcat-app1,浏覽器輸入 http://app1.micmiu.com:18080/examples/servlets/ 回車:

【SSO-CAS】SSO之CAS單點登入執行個體示範

看到上述界面表示tomcat-app1的基本安裝配置已經成功。

接下來複制 client的lib包cas-client-core-3.2.1.jar到 tomcat-app1\webapps\examples\WEB-INF\lib\目錄下, 在tomcat-app1\webapps\examples\WEB-INF\web.xml 檔案中增加如下内容:

<code>&lt;!-- ======================== 單點登入開始 ======================== --&gt;</code>

<code>        </code><code>&lt;!-- 用于單點退出,該過濾器用于實作單點登出功能,可選配置--&gt;</code>

<code>        </code><code>&lt;</code><code>listener</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>listener-class</code><code>&gt;org.jasig.cas.client.session.SingleSignOutHttpSessionListener&lt;/</code><code>listener-class</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>listener</code><code>&gt;</code>

<code>6</code>

<code>7</code>

<code>        </code><code>&lt;!-- 該過濾器用于實作單點登出功能,可選配置。 --&gt;</code>

<code>8</code>

<code>        </code><code>&lt;</code><code>filter</code><code>&gt;</code>

<code>9</code>

<code>            </code><code>&lt;</code><code>filter-name</code><code>&gt;CAS Single Sign Out Filter&lt;/</code><code>filter-name</code><code>&gt;</code>

<code>10</code>

<code>            </code><code>&lt;</code><code>filter-class</code><code>&gt;org.jasig.cas.client.session.SingleSignOutFilter&lt;/</code><code>filter-class</code><code>&gt;</code>

<code>11</code>

<code>        </code><code>&lt;/</code><code>filter</code><code>&gt;</code>

<code>12</code>

<code>        </code><code>&lt;</code><code>filter-mapping</code><code>&gt;</code>

<code>13</code>

<code>14</code>

<code>            </code><code>&lt;</code><code>url-pattern</code><code>&gt;/*&lt;/</code><code>url-pattern</code><code>&gt;</code>

<code>15</code>

<code>        </code><code>&lt;/</code><code>filter-mapping</code><code>&gt;</code>

<code>16</code>

<code>17</code>

<code>18</code>

<code>            </code><code>&lt;</code><code>filter-name</code><code>&gt;CAS Filter&lt;/</code><code>filter-name</code><code>&gt;</code>

<code>19</code>

<code>            </code><code>&lt;</code><code>filter-class</code><code>&gt;org.jasig.cas.client.authentication.AuthenticationFilter&lt;/</code><code>filter-class</code><code>&gt;</code>

<code>20</code>

<code>            </code><code>&lt;</code><code>init-param</code><code>&gt;</code>

<code>21</code>

<code>                </code><code>&lt;</code><code>param-name</code><code>&gt;casServerLoginUrl&lt;/</code><code>param-name</code><code>&gt;</code>

<code>22</code>

<code>23</code>

<code>            </code><code>&lt;/</code><code>init-param</code><code>&gt;</code>

<code>24</code>

<code>25</code>

<code>                </code><code>&lt;</code><code>param-name</code><code>&gt;serverName&lt;/</code><code>param-name</code><code>&gt;</code>

<code>26</code>

<code>27</code>

<code>28</code>

<code>29</code>

<code>30</code>

<code>31</code>

<code>32</code>

<code>33</code>

<code>        </code><code>&lt;!-- 該過濾器負責對Ticket的校驗工作,必須啟用它 --&gt;</code>

<code>34</code>

<code>35</code>

<code>            </code><code>&lt;</code><code>filter-name</code><code>&gt;CAS Validation Filter&lt;/</code><code>filter-name</code><code>&gt;</code>

<code>36</code>

<code>            </code><code>&lt;</code><code>filter-class</code><code>&gt;</code>

<code>37</code>

<code>                </code><code>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter&lt;/</code><code>filter-class</code><code>&gt;</code>

<code>38</code>

<code>39</code>

<code>                </code><code>&lt;</code><code>param-name</code><code>&gt;casServerUrlPrefix&lt;/</code><code>param-name</code><code>&gt;</code>

<code>40</code>

<code>41</code>

<code>42</code>

<code>43</code>

<code>44</code>

<code>45</code>

<code>46</code>

<code>47</code>

<code>48</code>

<code>49</code>

<code>50</code>

<code>51</code>

<code>52</code>

<code>        </code><code>&lt;!--</code>

<code>53</code>

<code>            </code><code>該過濾器負責實作HttpServletRequest請求的包裹,</code>

<code>54</code>

<code>            </code><code>比如允許開發者通過HttpServletRequest的getRemoteUser()方法獲得SSO登入使用者的登入名,可選配置。</code>

<code>55</code>

<code>        </code><code>--&gt;</code>

<code>56</code>

<code>57</code>

<code>            </code><code>&lt;</code><code>filter-name</code><code>&gt;CAS HttpServletRequest Wrapper Filter&lt;/</code><code>filter-name</code><code>&gt;</code>

<code>58</code>

<code>59</code>

<code>                </code><code>org.jasig.cas.client.util.HttpServletRequestWrapperFilter&lt;/</code><code>filter-class</code><code>&gt;</code>

<code>60</code>

<code>61</code>

<code>62</code>

<code>63</code>

<code>64</code>

<code>65</code>

<code>66</code>

<code>    </code><code>&lt;!--</code>

<code>67</code>

<code>        </code><code>該過濾器使得開發者可以通過org.jasig.cas.client.util.AssertionHolder來擷取使用者的登入名。</code>

<code>68</code>

<code>        </code><code>比如AssertionHolder.getAssertion().getPrincipal().getName()。</code>

<code>69</code>

<code>70</code>

<code>71</code>

<code>            </code><code>&lt;</code><code>filter-name</code><code>&gt;CAS Assertion Thread Local Filter&lt;/</code><code>filter-name</code><code>&gt;</code>

<code>72</code>

<code>            </code><code>&lt;</code><code>filter-class</code><code>&gt;org.jasig.cas.client.util.AssertionThreadLocalFilter&lt;/</code><code>filter-class</code><code>&gt;</code>

<code>73</code>

<code>74</code>

<code>75</code>

<code>76</code>

<code>77</code>

<code>78</code>

<code>79</code>

<code>        </code><code>&lt;!-- ======================== 單點登入結束 ======================== --&gt;</code>

有關cas-client的web.xml修改的詳細說明見官網介紹:

<a href="https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml" target="_blank">https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml</a>

6.3 安裝配置 tomcat-app2

解壓apache-tomcat-6.0.29.tar.gz并重命名後的路徑為 G:\sso\tomcat-app2,修改tomcat的啟動端口,在檔案 conf/server.xml檔案找到如下内容:

<code>&lt;</code><code>Connector</code> <code>port</code><code>=</code><code>"28080"</code> <code>protocol</code><code>=</code><code>"HTTP/1.1"</code>

<code>               </code><code>redirectPort</code><code>=</code><code>"28443"</code> <code>/&gt;</code>

<code>&lt;</code><code>Connector</code> <code>port</code><code>=</code><code>"28009"</code> <code>protocol</code><code>=</code><code>"AJP/1.3"</code> <code>redirectPort</code><code>=</code><code>"28443"</code> <code>/&gt;</code>

啟動tomcat-app2,浏覽器輸入 http://app2.micmiu.com:28080/examples/servlets/ 回車,按照上述6.2中的方法驗證是否成功。

同6.2中的複制 client的lib包cas-client-core-3.2.1.jar到 tomcat-app2\webapps\examples\WEB-INF\lib\目錄下, 在tomcat-app2\webapps\examples\WEB-INF\web.xml 檔案中增加如下内容:

<code>            </code><code>該過濾器使得開發者可以通過org.jasig.cas.client.util.AssertionHolder來擷取使用者的登入名。</code>

<code>            </code><code>比如AssertionHolder.getAssertion().getPrincipal().getName()。</code>

啟動之前配置好的三個tomcat分别為:tomcat-cas、tomcat-app1、tomcat-app2.

7.1  基本的測試

預期流程: 打開app1 url —-&gt; 跳轉cas server 驗證 —-&gt; 顯示app1的應用 —-&gt; 打開app2 url —-&gt; 顯示app2應用 —-&gt; 登出cas server —-&gt; 打開app1/app2 url —-&gt; 重新跳轉到cas server 驗證.

打開浏覽器位址欄中輸入:http://app1.micmiu.com:18080/examples/servlets/servlet/HelloWorldExample,回車:

【SSO-CAS】SSO之CAS單點登入執行個體示範

跳轉到驗證頁面:

【SSO-CAS】SSO之CAS單點登入執行個體示範

驗證通過後顯示如下:

【SSO-CAS】SSO之CAS單點登入執行個體示範

此時通路app2就不再需要驗證:

【SSO-CAS】SSO之CAS單點登入執行個體示範

位址欄中輸入:https://demo.micmiu.com:8443/cas/logout,回車顯示:

【SSO-CAS】SSO之CAS單點登入執行個體示範

上述表示 認證登出成功,此時如果再通路 : http://app1.micmiu.com:18080/examples/servlets/servlet/HelloWorldExample 或 http://app2.micmiu.com:28080/examples/servlets/servlet/HelloWorldExample 需要重新進行認證。

7.2  擷取登入使用者的資訊

修改類:webapps\examples\WEB-INF\classes\HelloWorldExample.java 後重新編譯并替換 webapps\examples\WEB-INF\classes\HelloWorldExample.class檔案。

 HelloWorldExample.java 修改後的代碼如下:

<code>import</code> <code>java.io.*;</code>

<code>import</code> <code>java.util.*;</code>

<code>import</code> <code>java.util.Map.Entry;</code>

<code>import</code> <code>javax.servlet.*;</code>

<code>import</code> <code>javax.servlet.http.*;</code>

<code>import</code> <code>org.jasig.cas.client.authentication.AttributePrincipal;</code>

<code>import</code> <code>org.jasig.cas.client.util.AbstractCasFilter;</code>

<code>import</code> <code>org.jasig.cas.client.validation.Assertion;</code>

<code>/**</code>

<code> </code><code>* CAS simple Servlet</code>

<code> </code><code>*</code>

<code> </code><code>*/</code>

<code>public</code> <code>class</code> <code>HelloWorldExample </code><code>extends</code> <code>HttpServlet {</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>long</code> <code>serialVersionUID = -6593274907821061823L;</code>

<code>    </code><code>@SuppressWarnings</code><code>(</code><code>"unchecked"</code><code>)</code>

<code>    </code><code>public</code> <code>void</code> <code>doGet(HttpServletRequest request, HttpServletResponse response)</code>

<code>            </code><code>throws</code> <code>IOException, ServletException {</code>

<code>        </code><code>ResourceBundle rb = ResourceBundle.getBundle(</code><code>"LocalStrings"</code><code>,</code>

<code>                </code><code>request.getLocale());</code>

<code>        </code><code>response.setContentType(</code><code>"text/html"</code><code>);</code>

<code>        </code><code>PrintWriter out = response.getWriter();</code>

<code>        </code><code>out.println(</code><code>"&lt;html&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;head&gt;"</code><code>);</code>

<code>        </code><code>String title = rb.getString(</code><code>"helloworld.title"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;title&gt;"</code> <code>+ title + </code><code>"&lt;/title&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;/head&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;body bgcolor=\"white\"&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;a href=\"../helloworld.html\"&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;img src=\"../images/code.gif\" height=24 "</code>

<code>                </code><code>+ </code><code>"width=24 align=right border=0 alt=\"view code\"&gt;&lt;/a&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;a href=\"../index.html\"&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;img src=\"../images/return.gif\" height=24 "</code>

<code>                </code><code>+ </code><code>"width=24 align=right border=0 alt=\"return\"&gt;&lt;/a&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;h1&gt;"</code> <code>+ title + </code><code>"&lt;/h1&gt;"</code><code>);</code>

<code>        </code><code>Assertion assertion = (Assertion) request.getSession().getAttribute(</code>

<code>                </code><code>AbstractCasFilter.CONST_CAS_ASSERTION);</code>

<code>        </code><code>if</code> <code>(</code><code>null</code> <code>!= assertion) {</code>

<code>            </code><code>out.println(</code><code>" Log | ValidFromDate =:"</code>

<code>                    </code><code>+ assertion.getValidFromDate() + </code><code>"&lt;br&gt;"</code><code>);</code>

<code>            </code><code>out.println(</code><code>" Log | ValidUntilDate =:"</code>

<code>                    </code><code>+ assertion.getValidUntilDate() + </code><code>"&lt;br&gt;"</code><code>);</code>

<code>            </code><code>Map&lt;Object, Object&gt; attMap = assertion.getAttributes();</code>

<code>            </code><code>out.println(</code><code>" Log | getAttributes Map size = "</code> <code>+ attMap.size()</code>

<code>                    </code><code>+ </code><code>"&lt;br&gt;"</code><code>);</code>

<code>            </code><code>for</code> <code>(Entry&lt;Object, Object&gt; entry : attMap.entrySet()) {</code>

<code>                </code><code>out.println(</code><code>"     | "</code> <code>+ entry.getKey() + </code><code>"=:"</code>

<code>                        </code><code>+ entry.getValue() + </code><code>"&lt;br&gt;"</code><code>);</code>

<code>            </code><code>}</code>

<code>            </code><code>AttributePrincipal principal = assertion.getPrincipal();</code>

<code>            </code><code>// AttributePrincipal principal = (AttributePrincipal) request</code>

<code>            </code><code>// .getUserPrincipal();</code>

<code>            </code><code>String username = </code><code>null</code><code>;</code>

<code>            </code><code>out.print(</code><code>" Log | UserName:"</code><code>);</code>

<code>            </code><code>if</code> <code>(</code><code>null</code> <code>!= principal) {</code>

<code>                </code><code>username = principal.getName();</code>

<code>                </code><code>out.println(</code><code>"&lt;span style='color:red;'&gt;"</code> <code>+ username</code>

<code>                        </code><code>+ </code><code>"&lt;/span&gt;&lt;br&gt;"</code><code>);</code>

<code>        </code><code>}</code>

<code>        </code><code>out.println(</code><code>"&lt;/body&gt;"</code><code>);</code>

<code>        </code><code>out.println(</code><code>"&lt;/html&gt;"</code><code>);</code>

<code>    </code><code>}</code>

<code>80</code>

<code>}</code>

再進行上述測試顯示結果如下:

http://app1.micmiu.com:18080/examples/servlets/servlet/HelloWorldExample :

【SSO-CAS】SSO之CAS單點登入執行個體示範

http://app2.micmiu.com:28080/examples/servlets/servlet/HelloWorldExample

【SSO-CAS】SSO之CAS單點登入執行個體示範

從上述頁面可以看到通過認證的使用者名。

到此已經全部完成了CAS單點登入執行個體示範。