Tomcat認(rèn)證授權(quán)與簡單的SSO
回顧
在前幾天,我使用apache+tomcat搭建了一個集群,有一個簡單的網(wǎng)站應(yīng)用。http://my.oschina.net/xpbug/blog/197680。
今天在此基礎(chǔ)上,我要為其開發(fā)一個登錄頁面,并開啟網(wǎng)站的認(rèn)證和授權(quán)。
基本概念
在動手之前,我需要先了解網(wǎng)站認(rèn)證和授權(quán)的幾個基本組成部分的概念。是什么構(gòu)成了網(wǎng)站應(yīng)用的認(rèn)證和授權(quán)?
- Realm - 翻譯過來叫做“域”。Realm是web容器所持有的用戶集合。無論tomcat, glassfish,jboss還是websphere,均是符合j2ee規(guī)范或最佳實現(xiàn)。Realm是需要網(wǎng)站系統(tǒng)管理員進(jìn)行配置的。常見的Realm 有三種:數(shù)據(jù)庫,LDAP和文件系統(tǒng)。數(shù)據(jù)庫realm是指用戶信息都存在數(shù)據(jù)庫中,Ldap則存放在ldap中,文件系統(tǒng)的realm則是用戶信息按照 一定的格式,存放于文件中。Realm是認(rèn)證的關(guān)鍵,web容器會將用戶輸入的用戶名和密碼跟realm中的用戶信息進(jìn)行比對。當(dāng)比對成功的時候,認(rèn)證也 就成功了。
- Role - 角色。 這是授權(quán)的部分。當(dāng)Realm被配好以后,系統(tǒng)管理員可以為realm中的用戶分配角色。建立用戶role-mapping. 每次用戶通過web容器的認(rèn)證以后,web容器會將其role信息也查詢出來,放入用戶信息中。
- security-constraint。 這是web應(yīng)用web.xml中的配置。 一個web應(yīng)用將在web.xml中聲明其受保護(hù)的資源,并聲明某種角色可以訪問受保護(hù)的資源。
- 認(rèn)證方式。 一般認(rèn)證方式分為Basic Authentication(BA)和Form-based Authentication(FBA)。
- 密碼加密。一旦黑客黑了服務(wù)器,明文密碼就會全部暴露了。所以,需要對密碼進(jìn)行加密存放。一般使用MD5 SHA算法對密碼進(jìn)行加密。
以上1,2,5是由網(wǎng)站管理員來配置開發(fā)。3,4是由網(wǎng)站開發(fā)人員來配置和開發(fā)的。
本實驗簡介
在前篇實驗結(jié)果的基礎(chǔ)上,為網(wǎng)站開啟ldap認(rèn)證,并制作一個login頁面。實驗所用的ldap軟件為開源的openLDAP for windows版本。
OpenLDAP安裝配置
- 下載并安裝軟件 http://sourceforge.net/projects/openldapwindows/ OpenLDAP會被安裝成windows service.
- 修改slapd.conf,聲明自己的后綴和管理員. 重啟service.
- database bdb
- suffix "dc=mycompany,dc=com"
- rootdn "cn=admin,dc=mycompany,dc=com"
- rootpw admin
- 創(chuàng)建my.ldif文件,借用tomcat文檔中的案例,文件內(nèi)容如下。其中定義了兩個用戶,和兩個角色。
- # Define top-level entry
- dn: dc=mycompany,dc=com
- objectClass: dcObject
- objectClass: organization
- o: mycompany
- dc:mycompany
- # Define an entry to contain people
- # searches for users are based on this entry
- dn: ou=people,dc=mycompany,dc=com
- objectClass: organizationalUnit
- ou: people
- # Define a user entry
- dn: uid=jjones,ou=people,dc=mycompany,dc=com
- objectClass: inetOrgPerson
- uid: jjones
- sn: jones
- cn: janet jones
- mail: j.jones@mycompany.com
- userPassword: janet
- # Define a user entry for Fred Bloggs
- dn: uid=fbloggs,ou=people,dc=mycompany,dc=com
- objectClass: inetOrgPerson
- uid: fbloggs
- sn: bloggs
- cn: fred bloggs
- mail: f.bloggs@mycompany.com
- userPassword: fred
- # Define an entry to contain LDAP groups
- # searches for roles are based on this entry
- dn: ou=groups,dc=mycompany,dc=com
- objectClass: organizationalUnit
- ou: groups
- # Define an entry for the "red" role
- dn: cn=red,ou=groups,dc=mycompany,dc=com
- objectClass: groupOfUniqueNames
- cn: red
- uniqueMember: uid=jjones,ou=people,dc=mycompany,dc=com
- uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com
- # Define an entry for the "black" role
- dn: cn=black,ou=groups,dc=mycompany,dc=com
- objectClass: groupOfUniqueNames
- cn: black
- uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com
-
運行命令 slapadd.exe -l my.ldif
-
驗證條目添加成功,運行查詢命令 ldapsearch.exe -x -b "dc=mycompany,dc=com" "objectClass=*"
OpenLDAP安裝配置成功,我有了下面兩個用戶:
|
|
|
|
|
|
|
|
|
#p#
為Tomcat配置Realm
tomcat的realm可以配置在server.xml中的<engine>, <host>和<context>下面。分別表示realm的作用范圍。我抱著從簡的態(tài)度,將realm配置 在<engine>下面,這樣,整個tomcat上的application都可以使用此realm. 在server.xml的<engine>下面替換老的realm,添加如下代碼:
- <!-- <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
- resourceName="UserDatabase"/>
- -->
- <Realm className="org.apache.catalina.realm.JNDIRealm"
- connectionName="cn=admin,dc=mycompany,dc=com"
- connectionPassword="admin"
- connectionURL="ldap://localhost:389"
- userPassword="userPassword"
- userPattern="uid={0},ou=people,dc=mycompany,dc=com"
- <!--userRoleName="memberOf"-->
- roleBase="ou=groups,dc=mycompany,dc=com"
- roleName="cn"
- roleSearch="(uniqueMember={0})"
- />
重啟tomcat集群。
為Web應(yīng)用配置Basic Authentication
我要為https://www.test0.com/sessiontest/successful.jsp配置BA,只有role=red才可以訪問此頁面。
修改web應(yīng)用的web.xml,添加如下代碼:
- <security-constraint>
- <web-resource-collection>
- <web-resource-name>result</web-resource-name>
- <url-pattern>/successful.jsp</url-pattern>
- </web-resource-collection>
- <auth-constraint>
- <role-name>red</role-name>
- </auth-constraint>
- </security-constraint>
- <login-config>
- <auth-method>BASIC</auth-method>
- <realm-name>tomcat</realm-name>
- </login-config>
- <security-role>
- <role-name>red</role-name>
- </security-role>
- <security-role>
- <role-name>black</role-name>
- </security-role>
然后重新打包部署sessiontest.war. 對網(wǎng)站進(jìn)行測試,select.jsp是可以任意訪問的,當(dāng)點擊submit以后,必須對瀏覽器彈出的BA認(rèn)證框輸入用戶名和密碼才能post成功。
為Web應(yīng)用配置Form-based Authentication
這次,我們要設(shè)計一個登錄頁面。用戶可以隨意瀏覽購物車,選擇想要的東西,但當(dāng)用戶點擊submit的時候,我們需要用戶必須登錄,才能提交訂單。所以,我們需要對頁面https://www.test0.com/sessiontest/successful.jsp進(jìn)行FBA保護(hù)。
首先設(shè)計一個登錄頁面login.html
- <form action="j_security_check" method="post">
- Username<input type="text" name="j_username" /><br />
- Password<input type="password" name="j_password" /><br />
- <input type="submit" value="login" />
- </form>
注意form中的action已經(jīng)user和password的input的name屬 性,“j_security_check","j_username"和"j_password"這些事固定的,嚴(yán)格遵循J2EE規(guī)范。將 login.html放入select.jsp同級目錄下。
關(guān)于logout,我就不做設(shè)計了,很簡單,只需要執(zhí)行session.invalidate(),然后跳轉(zhuǎn)到登出頁面即可。
接下來,我們修改web.xml,配置FBA. 這里只需要替換之前BA中的的<login-config>:
- <login-config>
- <auth-method>FORM</auth-method>
- <form-login-config>
- <form-login-page>/login.html</form-login-page>
- <form-error-page>/login.html</form-error-page>
- </form-login-config>
- </login-config>
將web應(yīng)用重新打包部署。重啟tomcat。
注意:使用mod_proxy_balancer +mod_ajp+ AJP的方式連接tomcat,存在著一個未知錯誤。當(dāng)用戶沒有登錄,訪問被保護(hù)資源的時候,按照常理,瀏覽器會顯示我們配置好的login form。但AJP和tomcat之間的通信會在此斷掉。這可能是windows版本的問題,也可能是mod_ajp和tomcat存在缺陷??傊?,花了 一天的時間,也沒研究出成果。google上面類似的問題挺多,可惜都沒答案。我會開啟tomcat的log再仔細(xì)研究到底發(fā)生了什么。
根據(jù)以往老版本,大部分使用mod_jk+ajp的方式行的通,有時間的同學(xué)可以嘗試mod_jk.
mod_ajp+ajp的方式只是卡在了FBA上,其它的任何資源訪問,都沒問題,為了讓FBA工作起來,不得已,我將balancer修改成了http模式。
- <Proxy balancer://mycluster>
- BalancerMember http://127.0.0.1:8080 loadfactor=1 route=node1
- BalancerMember http://127.0.0.1:8081 loadfactor=1 route=node2
- ProxySet stickysession=JSESSIONID
- ProxySet lbmethod=byrequests
- </Proxy>
此外,還有一個陷阱。tomcat在做完j_security_check以后,會重定向到http,無論之前是https. 當(dāng)然我們可以通過配置server.xml和web.xml使其重定向到Https,但一般不推薦這樣做,因為這很可能導(dǎo)致循環(huán)重定向。
一般的做法是在web中添加一個filter,專門負(fù)責(zé)http和https的切換。又或者在apache中配置重定向。在http-vhosts.conf中添加:
- <VirtualHost *:80>
- ServerAdmin joey
- ServerName www.test0.com
- ErrorLog "logs/errlog"
- CustomLog "logs/accesslog" common
- RewriteEngine on
- RewriteRule ^/?sessiontest/(.*) https://%{SERVER_NAME}/sessiontest/$1 [R,L]
- </VirtualHost>
到此,網(wǎng)站可以在FBA的模式下正常運行了。
#p#
FBA的缺陷
在J2EE的規(guī)范中,F(xiàn)BA存在著很大的缺陷。列舉如下:
-
login的過程無法被干預(yù)。我們無法通過添加filter的形式進(jìn)行干預(yù)。login完全交給web容器處理,頁面也是有web容器負(fù)責(zé)展示。
-
沒有l(wèi)ogin地址,用戶無法bookmark一個Login頁面。直接訪問login.html是無法提交form的。login只能在訪問受保護(hù)資源的時候才會被觸發(fā)。
很不幸的是,tomcat完全遵循了J2EE關(guān)于FBA的規(guī)范,這使得FBA很不實用。 WebSphere Application Server則對規(guī)范進(jìn)行了變通,使得以上2個缺陷都被除去了。 Jboss, Glassfish和weblogic則不是很清楚,需要了解的同學(xué)可以去查看其文檔。
Tomcat build-in SSO
Tomcat本身集成SSO(Single Sign On)解決方案。如果一個tomcat上,部署了多個應(yīng)用,這時候可以使用tomcat自身的SSO解決方案。如果系統(tǒng)跨JVM甚至跨平臺,則需要一套復(fù) 雜的SSO解決方案。復(fù)雜的SSO解決方案需要自己開發(fā),或者使用第三方框架,如CAS, OPENSSO等。
這次的實驗,僅僅關(guān)注tomcat自帶的SSO方案。首先需要說明,tomcat自帶的SSO的必要前提是:
- 必須是Tomcat自帶的認(rèn)證方式:BA, FBA, Degist,client-cert
- 必須是在同一個tomcat vhost下。
- 同一個JVM中。
- 所有的應(yīng)用必須使用同一個domain。
- 需要cookie支持。cookie中會被插入JSESSIONIDSSO
- 所有應(yīng)用必須使用形同的realm。
接下來,我將重新復(fù)制一份新的tomcat, 取名tomcat3. 在tomcat3\webApps下面,tomcat自帶一個web應(yīng)用,叫做examples,其下/examples/jsp/security /protected/index.jsp是受FBA保護(hù)的。
修改server.xml,添加realm
- <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
- resourceName="UserDatabase"/>-->
- <Realm className="org.apache.catalina.realm.JNDIRealm"
- connectionName="cn=admin,dc=mycompany,dc=com"
- connectionPassword="admin"
- connectionURL="ldap://localhost:389"
- userPassword="userPassword"
- userPattern="uid={0},ou=people,dc=mycompany,dc=com"
- roleBase="ou=groups,dc=mycompany,dc=com"
- roleName="cn"
- roleSearch="(uniqueMember={0})"
- />
繼續(xù)修改server.xml, 打來host下面的SSO配置:
- <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
然后修改examples下面的web.xml, 將受保護(hù)的資源的允許訪問角色修改成:
- <auth-constraint>
- <role-name>red</role-name>
- </auth-constraint>
這樣,tomcat3下面就有一個使用OpenLDAP認(rèn)證的應(yīng)用examples了。接下來,我將examples復(fù)制一份,取名叫 examples2,放在相同目錄下?,F(xiàn)在tomcat3下面存在兩個應(yīng)用examples和examples2,它們使用同一個realm認(rèn)證。
啟動tomcat3, 訪問http://localhost:8080/examples/jsp/security/protected/index.jsp和http://localhost:8080/examples2/jsp/security/protected/index.jsp, 發(fā)現(xiàn)只需要登錄其中一個,另一個不再需要登錄。
接下來
由于FBA的缺陷,我們需要自己制作login機制。實際上很多互聯(lián)網(wǎng)網(wǎng)站都是自己的login機制。我將借助第三方認(rèn)證工具,比如SecurityFilter或Spring來重新制作login。鏈接稍后貼上。
原文鏈接:http://my.oschina.net/xpbug/blog/198765