在前几天,我使用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)