在前幾天,我使用apache+tomcat搭建了一個集羣,有一個簡單的網站應用。Apache httpd與tomcat集羣。css
今天在此基礎上,我要爲其開發一個登陸頁面,並開啓網站的認證和受權。html
在動手以前,我須要先了解網站認證和受權的幾個基本組成部分的概念。是什麼構成了網站應用的認證和受權?java
Realm - 翻譯過來叫作「域」。Realm是web容器所持有的用戶集合。不管tomcat, glassfish,jboss仍是websphere,均是符合j2ee規範或最佳實現。Realm是須要網站系統管理員進行配置的。常見的Realm有三種:數據庫,LDAP和文件系統。數據庫realm是指用戶信息都存在數據庫中,Ldap則存放在ldap中,文件系統的realm則是用戶信息按照必定的格式,存放於文件中。Realm是認證的關鍵,web容器會將用戶輸入的用戶名和密碼跟realm中的用戶信息進行比對。當比對成功的時候,認證也就成功了。node
Role - 角色。 這是受權的部分。當Realm被配好之後,系統管理員能夠爲realm中的用戶分配角色。創建用戶role-mapping. 每次用戶經過web容器的認證之後,web容器會將其role信息也查詢出來,放入用戶信息中。web
security-constraint。 這是web應用web.xml中的配置。 一個web應用將在web.xml中聲明其受保護的資源,並聲明某種角色能夠訪問受保護的資源。算法
認證方式。 通常認證方式分爲Basic Authentication(BA)和Form-based Authentication(FBA)。數據庫
密碼加密。一旦黑客黑了服務器,明文密碼就會所有暴露了。因此,須要對密碼進行加密存放。通常使用MD5 SHA算法對密碼進行加密。apache
以上1,2,5是由網站管理員來配置開發。3,4是由網站開發人員來配置和開發的。
c#
在前篇實驗結果的基礎上,爲網站開啓ldap認證,並製做一個login頁面。實驗所用的ldap軟件爲開源的openLDAP for windows版本。windows
下載並安裝軟件 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
建立my.ldif文件,借用tomcat文檔中的案例,文件內容以下。其中定義了兩個用戶,和兩個角色。
# 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安裝配置成功,我有了下面兩個用戶:
uid\group | red | black |
jjones | Y | Y |
fbloggs |
Y |
tomcat的realm能夠配置在server.xml中的<engine>, <host>和<context>下面。分別表示realm的做用範圍。我抱着從簡的態度,將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集羣。
我要爲https://www.test0.com/sessiontest/successful.jsp配置BA,只有role=red才能夠訪問此頁面。
修改web應用的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. 對網站進行測試,select.jsp是能夠任意訪問的,當點擊submit之後,必須對瀏覽器彈出的BA認證框輸入用戶名和密碼才能post成功。
此次,咱們要設計一個登陸頁面。用戶能夠隨意瀏覽購物車,選擇想要的東西,但當用戶點擊submit的時候,咱們須要用戶必須登陸,才能提交訂單。因此,咱們須要對頁面https://www.test0.com/sessiontest/successful.jsp進行FBA保護。
首先設計一個登陸頁面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已經user和password的input的name屬性,「j_security_check","j_username"和"j_password"這些事固定的,嚴格遵循J2EE規範。將login.html放入select.jsp同級目錄下。
關於logout,我就不作設計了,很簡單,只須要執行session.invalidate(),而後跳轉到登出頁面便可。
接下來,咱們修改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應用從新打包部署。重啓tomcat。
注意:使用mod_proxy_balancer +mod_ajp+ AJP的方式鏈接tomcat,存在着一個未知錯誤。當用戶沒有登陸,訪問被保護資源的時候,按照常理,瀏覽器會顯示咱們配置好的login form。但AJP和tomcat之間的通訊會在此斷掉。這多是windows版本的問題,也多是mod_ajp和tomcat存在缺陷。總之,花了一天的時間,也沒研究出成果。google上面相似的問題挺多,惋惜都沒答案。我會開啓tomcat的log再仔細研究到底發生了什麼。
根據以往老版本,大部分使用mod_jk+ajp的方式行的通,有時間的同窗能夠嘗試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. 固然咱們能夠經過配置server.xml和web.xml使其重定向到Https,但通常不推薦這樣作,由於這極可能致使循環重定向。
通常的作法是在web中添加一個filter,專門負責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>
到此,網站能夠在FBA的模式下正常運行了。
在J2EE的規範中,FBA存在着很大的缺陷。列舉以下:
login的過程沒法被幹預。咱們沒法經過添加filter的形式進行干預。login徹底交給web容器處理,頁面也是有web容器負責展現。
沒有login地址,用戶沒法bookmark一個Login頁面。直接訪問login.html是沒法提交form的。login只能在訪問受保護資源的時候纔會被觸發。
很不幸的是,tomcat徹底遵循了J2EE關於FBA的規範,這使得FBA很不實用。 WebSphere Application Server則對規範進行了變通,使得以上2個缺陷都被除去了。 Jboss, Glassfish和weblogic則不是很清楚,須要瞭解的同窗能夠去查看其文檔。
Tomcat自己集成SSO(Single Sign On)解決方案。若是一個tomcat上,部署了多個應用,這時候可使用tomcat自身的SSO解決方案。若是系統跨JVM甚至跨平臺,則須要一套複雜的SSO解決方案。複雜的SSO解決方案須要本身開發,或者使用第三方框架,如CAS, OPENSSO等。
此次的實驗,僅僅關注tomcat自帶的SSO方案。首先須要說明,tomcat自帶的SSO的必要前提是:
必須是Tomcat自帶的認證方式:BA, FBA, Degist,client-cert
必須是在同一個tomcat vhost下。
同一個JVM中。
全部的應用必須使用同一個domain。
須要cookie支持。cookie中會被插入JSESSIONIDSSO
全部應用必須使用形同的realm。
接下來,我將從新複製一份新的tomcat, 取名tomcat3. 在tomcat3\webApps下面,tomcat自帶一個web應用,叫作examples,其下/examples/jsp/security/protected/index.jsp是受FBA保護的。
修改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})" />
繼續修改server.xml, 打來host下面的SSO配置:
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
而後修改examples下面的web.xml, 將受保護的資源的容許訪問角色修改爲:
<auth-constraint> <role-name>red</role-name> </auth-constraint>
這樣,tomcat3下面就有一個使用OpenLDAP認證的應用examples了。接下來,我將examples複製一份,取名叫examples2,放在相同目錄下。如今tomcat3下面存在兩個應用examples和examples2,它們使用同一個realm認證。
啓動tomcat3, 訪問http://localhost:8080/examples/jsp/security/protected/index.jsp和http://localhost:8080/examples2/jsp/security/protected/index.jsp, 發現只須要登陸其中一個,另外一個再也不須要登陸。
因爲FBA的缺陷,咱們須要本身製做login機制。實際上不少互聯網網站都是本身的login機制。我將藉助第三方認證工具,好比SecurityFilter或Spring來從新制做login。進階-使用Spring Security3.2搭建LDAP認證受權和Remember-me(1)