??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲综合无码一区二区三区,亚洲AV无码专区亚洲AV伊甸园,精品国产日韩亚洲一区91http://www.tkk7.com/masen/category/29350.htmlzh-cnThu, 21 Feb 2008 08:22:12 GMTThu, 21 Feb 2008 08:22:12 GMT60session的详l解?/title><link>http://www.tkk7.com/masen/articles/180875.html</link><dc:creator>Masen</dc:creator><author>Masen</author><pubDate>Wed, 20 Feb 2008 07:48:00 GMT</pubDate><guid>http://www.tkk7.com/masen/articles/180875.html</guid><wfw:comment>http://www.tkk7.com/masen/comments/180875.html</wfw:comment><comments>http://www.tkk7.com/masen/articles/180875.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/masen/comments/commentRss/180875.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/masen/services/trackbacks/180875.html</trackback:ping><description><![CDATA[<p>Java基础Q关于session的详l解?<br /> 来源:不详 (2006-06-01 16:49:09) </p> <p> <br /> 一、术语session </p> <p>  在我的经验里Qsessionq个词被滥用的程度大概仅ơ于transactionQ更加有的是transaction与session在某些语境下的含义是相同的?/p> <p>  sessionQ中文经常翻译ؓ会话Q其本来的含义是指有始有l的一pd动作/消息Q比如打电话时从拿v电话拨号到挂断电话这中间的一pdq程可以UCZ个session?/p> <p>有时候我们可以看到这L?#8220;在一个浏览器会话期间Q?..”Q这里的会话一词用的就是其本义Q是指从一个浏览器H口打开到关闭这个期间①。最混ؕ的是“用户Q客L</p> <p>Q在一ơ会话期?#8221;q样一句话Q它可能指用L一pd动作Q一般情况下是同某个具体目的相关的一pd动作Q比如从d到选购商品到结账登样一个网上购物的q程</p> <p>Q有时候也被称Z个transactionQ,然而有时候也可能仅仅是指一ơ连接,也有可能是指含义①,其中的差别只能靠上下文来推断②?/p> <p>  然而当session一词与|络协议相关联时Q它又往往隐含?#8220;面向q接”??#8220;保持状?#8221;q样两个含义Q?#8220;面向q接”指的是在通信双方在通信之前要先建立一个通信</p> <p>的渠道,比如打电话,直到Ҏ接了电话通信才能开始,与此相对的是写信Q在你把信发出去的时候你q不能确认对方的地址是否正确Q通信渠道不一定能建立Q但对发信h</p> <p>来说Q通信已经开始了?#8220;保持状?#8221;则是指通信的一方能够把一pd的消息关联v来,使得消息之间可以互相依赖Q比如一个服务员能够认出再次光的老顾客ƈ且记得上</p> <p>ơ这个顾客还Ơ店里一块钱。这一cȝ例子?#8220;一个TCP session”或?#8220;一个POP3 session”③?/p> <p>  而到了web服务器蓬勃发展的时代Qsession在web开发语境下的语义又有了新的扩展Q它的含义是指一cȝ来在客户端与服务器之间保持状态的解决Ҏ④。有时?/p> <p>session也用来指q种解决Ҏ的存储结构,?#8220;把xxx保存在session?#8221;⑤。由于各U用于web开发的语言在一定程度上都提供了对这U解x案的支持Q所以在某种特定?/p> <p>a的语境下Qsession也被用来指代该语a的解x案,比如l常把Java里提供的javax.servlet.http.HttpSessionUCؓsession⑥?/p> <p>  鉴于q种混ؕ已不可改变,本文中session一词的q用也会Ҏ上下文有不同的含义,请大家注意分辨?/p> <p>  在本文中Q用中?#8220;览器会话期?#8221;来表辑֐义①Q?#8220;session机制”来表辑֐义④Q?#8220;session”表达含义⑤,使用具体?#8220;HttpSession”来表辑֐义⑥</p> <p>  二、HTTP协议与状态保?/p> <p>  HTTP协议本n是无状态的Q这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器h下蝲某些文gQ无论是客户端还是服务器都没有必要纪录彼此过ȝ?/p> <p>为,每一ơ请求之间都是独立的Q好比一个顾客和一个自动售货机或者一个普通的Q非会员Ӟ大卖Z间的关系一栗?/p> <p>  然而聪明(或者贪心?Q的Z很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用Q就像给有线电视加上Ҏ功能一栗这U需求一斚wqHTML逐步</p> <p>d了表单、脚本、DOM{客L行ؓQ另一斚w在服务器端则出现了CGI规范以响应客L的动态请求,作ؓ传输载体的HTTP协议也添加了文g上蝲、cookieq些Ҏ。其?/p> <p>cookie的作用就是ؓ了解决HTTP协议无状态的~陷所作出的努力。至于后来出现的session机制则是又一U在客户端与服务器之间保持状态的解决Ҏ?/p> <p>  让我们用几个例子来描qC下cookie和session机制之间的区别与联系。笔者曾l常ȝ一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠Q然而一ơ性消?杯咖啡的Z?/p> <p>乎其微,q时需要某U方式来U录某位֮的消Ҏ量。想象一下其实也无外乎下面的几种ҎQ?/p> <p>  1、该店的店员很厉宻I能记住每位顾客的消费数量Q只要顾客一走进咖啡店,店员q道该怎么对待了。这U做法就是协议本w支持状态?/p> <p>  2、发l顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每ơ消ҎQ如果顾客出C张卡片,则此ơ消费就会与以前或以后的消费相联pv来。这U做?/p> <p>是在客L保持状态?/p> <p>  3、发l顾客一张会员卡Q除了卡号之外什么信息也不纪录,每次消费Ӟ如果֮出示该卡片,则店员在店里的纪录本上找到这个卡号对应的U录d一些消费信息。这</p> <p>U做法就是在服务器端保持状态?/p> <p>  ׃HTTP协议是无状态的Q而出于种U考虑也不希望使之成ؓ有状态的Q因此,后面两种Ҏ成为现实的选择。具体来说cookie机制采用的是在客L保持状态的Ҏ</p> <p>Q而session机制采用的是在服务器端保持状态的Ҏ。同时我们也看到Q由于采用服务器端保持状态的Ҏ在客L也需要保存一个标识,所以session机制可能需要借助?/p> <p>cookie机制来达C存标识的目的Q但实际上它q有其他选择?/p> <p>  三、理解cookie机制 </p> <p>  cookie机制的基本原理就如上面的例子一L单,但是q有几个问题需要解冻I“会员?#8221;如何分发Q?#8220;会员?#8221;的内容;以及客户如何使用“会员?#8221;?/p> <p>  正统的cookie分发是通过扩展HTTP协议来实现的Q服务器通过在HTTP的响应头中加上一行特D的指示以提C浏览器按照指示生成相应的cookie。然而纯_的客户端脚本如</p> <p>JavaScript或者VBScript也可以生成cookie?/p> <p>  而cookie的用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器查所有存储的cookieQ如果某个cookie所声明的作用范围大于等于将要请求的资源所?/p> <p>的位|,则把该cookie附在h资源的HTTPh头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示Q如果某家分店还发行了自q会员卡,那么q这家店?/p> <p>时候除了要出示麦当劳的会员卡,q要出示q家店的会员卡?/p> <p>  cookie的内容主要包括:名字Q|q期旉Q\径和域?/p> <p>  其中域可以指定某一个域比如.google.comQ相当于d招牌Q比如宝z公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.comQ可以用</p> <p>飘柔来做比?/p> <p>  路径是跟在域名后面的URL路径Q比?或?foo{等Q可以用某飘柔专柜做比?/p> <p>  路径与域合在一起就构成了cookie的作用范围?/p> <p>  如果不设|过期时_则表C个cookie的生命期为浏览器会话期间Q只要关闭浏览器H口Qcookie消׃。这U生命期为浏览器会话期的cookie被称Z话cookie?/p> <p>会话cookie一般不存储在硬盘上而是保存在内存里Q当然这U行为ƈ不是规范规定的。如果设|了q期旉Q浏览器׃把cookie保存到硬盘上Q关闭后再次打开览器,q?/p> <p>些cookie仍然有效直到过讑֮的过期时间?/p> <p>  存储在硬盘上的cookie可以在不同的览器进E间׃nQ比如两个IEH口。而对于保存在内存里的cookieQ不同的览器有不同的处理方式。对于IEQ在一个打开的窗?/p> <p>上按Ctrl-NQ或者从文g菜单Q打开的窗口可以与原窗口共享,而用其他方式新开的IEq程则不能共享已l打开的窗口的内存cookieQ对于Mozilla Firefox0.8Q所有的q?/p> <p>E和标签都可以׃n同样的cookie。一般来说是用javascript的window.open打开的窗口会与原H口׃n内存cookie。浏览器对于会话cookie的这U只认cookie不认人的处理</p> <p>方式l常l采用session机制的web应用E序开发者造成很大的困扰?/p> <p>  下面是一个goolge讄cookie的响应头的例?/p> <p>HTTP/1.1 302 Found<br /> Location: http://www.google.com/intl/zh-CN/<br /> Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; </p> <p>domain=.google.com<br /> Content-Type: text/html</p> <p><br />   q是使用HTTPLookq个HTTP Sniffer软g来俘LHTTP通讯U录的一部分</p> <p> </p> <p>  览器在再次讉Kgoolge的资源时自动向外发送cookie</p> <p><br />   使用Firefox可以很容易的观察现有的cookie的?/p> <p>  使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理?/p> <p> </p> <p>  IE也可以设|在接受cookie前询?/p> <p><br />   q是一个询问接受cookie的对话框?/p> <p>  四、理解session机制</p> <p> session机制是一U服务器端的机制Q服务器使用一U类g散列表的l构Q也可能是使用散列表)来保存信息?/p> <p>  当程序需要ؓ某个客户端的h创徏一个session的时候,服务器首先检查这个客L的请求里是否已包含了一个session标识 - UCؓsession idQ如果已包含一?/p> <p>session id则说明以前已lؓ此客L创徏qsessionQ服务器按照session id把这个session索出来用(如果索不刎ͼ可能会新Z个)Q如果客Lh不包?/p> <p>session idQ则为此客户端创Z个sessionq且生成一个与此session相关联的session idQsession id的值应该是一个既不会重复Q植蝗菀妆徽业焦媛梢苑略斓淖址?/p> <p>飧鰏ession id被在本ơ响应中q回l客L保存?/p> <p>  保存q个session id的方式可以采用cookieQ这样在交互q程中浏览器可以自动的按照规则把q个标识发挥l服务器。一般这个cookie的名字都是类gSEEESIONIDQ?/p> <p>。比如weblogic对于web应用E序生成的cookieQJSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764Q它的名字就是JSESSIONID?/p> <p>  ׃cookie可以被h为的止Q必L其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一U技术叫做URL重写Q就是把session id直接</p> <p>附加在URL路径的后面,附加方式也有两种Q一U是作ؓURL路径的附加信息,表现形式为http://...../xxx;jsessionid=ByOK ... 99zWpBng!-145788764另一U是作ؓ查询?/p> <p>W串附加在URL后面Q表现Ş式ؓhttp://...../xxx?jsessionid=ByOK ... 99zWpBng!-145788764<br /> q两U方式对于用h说是没有区别的,只是服务器在解析的时候处理的方式不同Q采用第一U方式也有利于把session id的信息和正常E序参数区分开来?/p> <p>  Z在整个交互过E中始终保持状态,必d每个客户端可能请求的路径后面都包含这个session id?/p> <p>  另一U技术叫做表单隐藏字Dc就是服务器会自动修改表单,d一个隐藏字D,以便在表单提交时能够把session id传递回服务器?/p> <p>  q种技术现在已较少应用Q笔者接触过的很古老的iPlanet6(SunONE应用服务器的前n)׃用了q种技术。实际上q种技术可以简单的用对action应用URL重写来代ѝ?/p> <p>  在谈论session机制的时候,常常听到q样一U误?#8220;只要关闭览器,session消׃”。其实可以想象一下会员卡的例子,除非֮d对店家提出销卡,否则店家</p> <p>l对不会L删除֮的资料。对session来说也是一LQ除非程序通知服务器删除一个sessionQ否则服务器会一直保留,E序一般都是在用户做log off的时候发个指令去</p> <p>删除session。然而浏览器从来不会d在关闭之前通知服务器它要关闭Q因此服务器Ҏ不会有机会知道浏览器已经关闭Q之所以会有这U错觉,是大部分session机制?/p> <p>使用会话cookie来保存session idQ而关闭浏览器后这个session id消׃Q再ơ连接服务器时也无法找到原来的session。如果服务器讄的cookie被保存到盘上,?/p> <p>者用某U手D|写浏览器发出的HTTPh_把原来的session id发送给服务器,则再ơ打开览器仍然能够找到原来的session?/p> <p>  恰恰是由于关闭浏览器不会Dsession被删除,q服务器ؓseesion讄了一个失效时_当距dL上一ơ用session的时间超q这个失效时间时Q服务器可?/p> <p>认ؓ客户端已l停止了zdQ才会把session删除以节省存储空间?/p> <p>  五、理解javax.servlet.http.HttpSession</p> <p>  HttpSession是Javaq_对session机制的实现规范,因ؓ它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细</p> <p>微差异。这里我们以BEA的Weblogic Server8.1作ؓ例子来演C?/p> <p>  首先QWeblogic Server提供了一pd的参数来控制它的HttpSession的实玎ͼ包括使用cookie的开关选项Q用URL重写的开关选项Qsession持久化的讄Qsession失效</p> <p>旉的设|,以及针对cookie的各U设|,比如讄cookie的名字、\径、域Qcookie的生存时间等?/p> <p>  一般情况下Qsession都是存储在内存里Q当服务器进E被停止或者重启的时候,内存里的session也会被清I,如果讄了session的持久化Ҏ,服务器就会把session</p> <p>保存到硬盘上Q当服务器进E重新启动或q些信息能够被再次使用QWeblogic Server支持的持久性方式包括文件、数据库、客Lcookie保存和复制?/p> <p>  复制严格说来不算持久化保存,因ؓsession实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进E中Q这样即使某个服务器q程停止工作也仍</p> <p>然可以从其他q程中取得session?/p> <p>  cookie生存旉的设|则会媄响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解?/p> <p>  cookie的\径对于web应用E序来说是一个非帔R要的选项QWeblogic Server对这个选项的默认处理方式得它与其他服务器有明昄区别。后面我们会专题讨论?/p> <p>  关于session的设|参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869</p> <p>  六、HttpSession常见问题</p> <p>  Q在本小节中session的含义ؓ⑤和⑥的混合Q?/p> <p>  1、session在何时被创徏</p> <p>  一个常见的误解是以为session在有客户端访问时p创徏Q然而事实是直到某server端程序调用HttpServletRequest.getSession(true)q样的语句时才被创徏Q注意如</p> <p>果JSP没有昄的?关闭sessionQ则JSP文g在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);q也是JSP?/p> <p>隐含的session对象的来历?/p> <p>  ׃session会消耗内存资源,因此Q如果不打算使用sessionQ应该在所有的JSP中关闭它?/p> <p>  2、session何时被删?/p> <p>  l合前面的讨论,session在下列情况下被删除a.E序调用HttpSession.invalidate();或b.距离上一ơ收到客L发送的session id旉间隔过了session的超时设|?</p> <p>或c.服务器进E被停止Q非持久sessionQ?/p> <p>  3、如何做到在览器关闭时删除session</p> <p>  严格的讲Q做不到q一炏V可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来</p> <p>删除session。但是对于浏览器崩溃或者强行杀死进E这些非常规手段仍然无能为力?/p> <p>  4、有个HttpSessionListener是怎么回事</p> <p>  你可以创Llistenerȝ控session的创建和销毁事Ӟ使得在发生这L事g时你可以做一些相应的工作。注意是session的创建和销毁动作触发listenerQ而不</p> <p>是相反。类似的与HttpSession有关的listenerq有HttpSessionBindingListenerQHttpSessionActivationListener和HttpSessionAttributeListener?</p> <p>5、存攑֜session中的对象必须是可序列化的?/p> <p>  不是必需的。要求对象可序列化只是ؓ了session能够在集中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在Weblogic Server?/p> <p>session中放|一个不可序列化的对象在控制C会收C个警告。我所用过的某个iPlanet版本如果session中有不可序列化的对象Q在session销毁时会有一个ExceptionQ很</p> <p>奇怪?/p> <p>  6、如何才能正的应付客户端禁止cookie的可能?/p> <p>  Ҏ有的URL使用URL重写Q包括超链接Qform的actionQ和重定向的URLQ具体做法参见[6]<br /> http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770</p> <p>  7、开两个览器窗口访问应用程序会使用同一个sessionq是不同的session</p> <p>  参见W三节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器Q不同的H口打开方式以及不同的cookie存储方式都会对这个问题的{案有媄响?/p> <p>  8、如何防止用h开两个览器窗口操作导致的session混ؕ</p> <p>  q个问题与防止表单多ơ提交是cM的,可以通过讄客户端的令牌来解冟뀂就是在服务器每ơ生成一个不同的idq回l客LQ同时保存在session里,客户端提交表?/p> <p>时必Lq个id也返回服务器Q程序首先比较返回的id与保存在session里的值是否一_如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表C层</p> <p>模式的部分。需要注意的是对于用javascript window.open打开的窗口,一般不讄q个idQ或者用单独的idQ以防主H口无法操作Q徏议不要再window.open打开的窗?/p> <p>里做修改操作Q这样就可以不用讄?/p> <p>  9、ؓ什么在Weblogic Server中改变session的值后要重新调用一ơsession.setValue<br /> 做这个动作主要是Z在集环境中提示Weblogic Server session中的值发生了改变Q需要向其他服务器进E复制新的session倹{?/p> <p>  10、ؓ什么session不见?/p> <p>  排除session正常失效的因素之外,服务器本w的可能性应该是微乎其微的,虽然W者在iPlanet6SP1加若q补丁的Solaris版本上倒也遇到q;览器插件的可能性次之,</p> <p>W者也遇到q?721插g造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题?/p> <p>  出现q一问题的大部分原因都是E序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨个问题?/p> <p>  七、跨应用E序的session׃n</p> <p>  常常有这L情况Q一个大目被分割成若干项目开发,Z能够互不q扰Q要求每个小目作ؓ一个单独的web应用E序开发,可是C最后突然发现某几个项目之</p> <p>间需要共享一些信息,或者想使用session来实现SSO(single sign on)Q在session中保存login的用户信息,最自然的要求是应用E序间能够访问彼此的session?/p> <p>  然而按照Servlet规范Qsession的作用范围应该仅仅限于当前应用程序下Q不同的应用E序之间是不能够互相讉KҎ的session的。各个应用服务器从实际效果上都遵?/p> <p>了这一规范Q但是实现的l节却可能各有不同,因此解决跨应用程序session׃n的方法也各不相同?/p> <p>  首先来看一下Tomcat是如何实现web应用E序之间session的隔ȝQ从Tomcat讄的cookie路径来看Q它对不同的应用E序讄的cookie路径是不同的Q这样不同的应用</p> <p>E序所用的session id是不同的Q因此即使在同一个浏览器H口里访问不同的应用E序Q发送给服务器的session id也可以是不同的?/p> <p> </p> <p>  Ҏq个Ҏ,我们可以推测Tomcat中session的内存结构大致如下?/p> <p> </p> <p>  W者以前用q的iPlanet也采用的是同L方式Q估计SunONE与iPlanet之间不会有太大的差别。对于这U方式的服务器,解决的思\很简单,实际实行h也不难。要?/p> <p>让所有的应用E序׃n一个session idQ要么让应用E序能够获得其他应用E序的session id?/p> <p>  iPlanet中有一U很单的Ҏ来实现共享一个session idQ那是把各个应用程序的cookie路径都设?Q实际上应该?NASAppQ对于应用程序来讲它的作用相当于根)</p> <p>?/p> <p>/NASApp </p> <p><br />   需要注意的是,操作׃n的session应该遵@一些编E约定,比如在session attribute名字的前面加上应用程序的前缀Q得setAttribute("name", "neo")变成</p> <p>setAttribute("app1.name", "neo")Q以防止命名I间冲突Q导致互相覆盖?/p> <p><br />   在Tomcat中则没有q么方便的选择。在Tomcat版本3上,我们q可以有一些手D|׃nsession。对于版?以上的TomcatQ目前笔者尚未发现简单的办法。只能借助于第?/p> <p>方的力量Q比如用文件、数据库、JMS或者客LcookieQURL参数或者隐藏字D늭手段?/p> <p>  我们再看一下Weblogic Server是如何处理session的?/p> <p> </p> <p><br />   从截屏画面上可以看到Weblogic ServerҎ有的应用E序讄的cookie的\径都?Q这是不是意味着在Weblogic Server中默认的可以共享session了呢Q然而一个小</p> <p>实验卛_证明即不同的应用程序用的是同一个sessionQ各个应用程序仍然只能访问自己所讄的那些属性。这说明Weblogic Server中的session的内存结构可能如?/p> <p> </p> <p>  对于q样一U结构,在session机制本n上来解决session׃n的问题应该是不可能的了。除了借助于第三方的力量,比如使用文g、数据库、JMS或者客LcookieQURL</p> <p>参数或者隐藏字D늭手段Q还有一U较为方便的做法Q就是把一个应用程序的session攑ֈServletContext中,q样另外一个应用程序就可以从ServletContext中取得前一个应</p> <p>用程序的引用。示例代码如下,</p> <p>  应用E序A</p> <p>context.setAttribute("appA", session); </p> <p>  应用E序B</p> <p>contextA = context.getContext("/appA");<br /> HttpSession sessionA = (HttpSession)contextA.getAttribute("appA"); </p> <p>  值得注意的是q种用法不可ULQ因为根据ServletContext的JavaDocQ应用服务器可以处于安全的原因对于context.getContext("/appA");q回I|以上做法?/p> <p>Weblogic Server 8.1中通过?/p> <p>  那么Weblogic ServerZ么要把所有的应用E序的cookie路径都设?呢?原来是ؓ了SSOQ凡是共享这个session的应用程序都可以׃n认证的信息。一个简单的实验?/p> <p>可以证明q一点,修改首先d的那个应用程序的描述Wweblogic.xmlQ把cookie路径修改?appA讉K另外一个应用程序会重新要求dQ即使是反过来,先访问cookie路径</p> <p>?的应用程序,再访问修改过路径的这个,虽然不再提示dQ但是登录的用户信息也会丢失。注意做q个实验时认证方式应该用FORMQ因为浏览器和web服务器对basic?/p> <p>证方式有其他的处理方式,W二ơ请求的认证不是通过session来实现的。具体请参看[7] secion 14.8 AuthorizationQ你可以修改所附的CZE序来做q些试验?/p> <p>  八、ȝ</p> <p>  session机制本nq不复杂Q然而其实现和配|上的灵zL却使得具体情况复杂多变。这也要求我们不能把仅仅某一ơ的l验或者某一个浏览器Q服务器的经验当作普遍?/p> <p>用的l验Q而是始终需要具体情况具体分析?<br /> (http://www.fanqiang.com)</p> <p><br />  </p> <p> <br />    <br />  相关文章 </p> <p> </p> <img src ="http://www.tkk7.com/masen/aggbug/180875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/masen/" target="_blank">Masen</a> 2008-02-20 15:48 <a href="http://www.tkk7.com/masen/articles/180875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数字{֐和哈希函?/title><link>http://www.tkk7.com/masen/articles/180703.html</link><dc:creator>Masen</dc:creator><author>Masen</author><pubDate>Tue, 19 Feb 2008 08:15:00 GMT</pubDate><guid>http://www.tkk7.com/masen/articles/180703.html</guid><wfw:comment>http://www.tkk7.com/masen/comments/180703.html</wfw:comment><comments>http://www.tkk7.com/masen/articles/180703.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/masen/comments/commentRss/180703.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/masen/services/trackbacks/180703.html</trackback:ping><description><![CDATA[<p align="center">   <span id="qbsddfm" class="da"><strong>数字{֐和哈希函?/strong></span></p> <strong>问题的提?/strong><br />    懂得一点公钥密码基知识的h都知道,发信息的人用自己的私钥对所发信息进行加? Encryption )Q接收信息者用发信者的公钥来解? Decryption )Q就可以保证信息的真实性、完整性和不可否认性。(注:q里提到的加密、解密是指密码运,其目的ƈ非信息保密。)那么Q我们也可以W统地说Q以上方法就已经辑ֈ了数字签名的目的。因为首先,U钥是发信者唯一持有的,别的MZ可能刉出q䆾密文来,所以可以相信这份密文以及对应的明文不是伪造的Q当Ӟ发信者n份的定q要通过数字证书来保证)Q出于同样原因,发信者也不能抵赖、否认自己曾l发q这份信息;另外Q信息在传输当中不可能被改Q因为如果有囄改,密文p不出来。这P用私钥加密,公钥解密的技术方法就可以代替传统{֐、盖章,保证了信息的真实性、完整性和不可否认性?<br />    但是Q这样做在实际用中却存在一个问题:要发的信息可能很长,非对U密码又比较复杂Q运量大,而ؓ了保证安全,U钥通常保存在USB Key或IC卡中Q加密运也是在Key或卡中进行。一般来_小的USB Key或IC卡中的微处理器都做得比较单而处理能力较弱,q样Q加密所用的旉׃很长而导致无法实用?br />    另外Q即使对于网站服务器而言Q虽然它的处理能力很强,但服务器要同时处理许许多多签名加密的事情Q也同样存在着加密耗时长系l效率低的问题?br />    有没有解册个问题的办法呢?有的Q常用的Ҏ是用哈希函数?br /> <br /> <strong>什么是哈希函数</strong><br />    哈希QHashQ函数在中文中有很多译名Q有些hҎHash的英文原意译?#8220;散列函数”?#8220;杂凑函数”Q有些hq脆把它韌?#8220;哈希函数”Q还有些人根据Hash函数的功能译?#8220;压羃函数”?#8220;消息摘要函数”?#8220;指纹函数”?#8220;单向散列函数”{等?<br /> 1、Hash法是把L长度的输入数据经q算法压~,输出一个尺寸小了很多的固定长度的数据,卛_希倹{哈希gUCؓ输入数据的数字指U(Digital FingerprintQ或消息摘要QMessage DigestQ等。Hash函数具备以下的性质Q?br /> 2、给定输入数据,很容易计出它的哈希|<br /> 3、反q来Q给定哈希|倒推入数据则很难Q计上不可行。这是哈希函数的单向性,在技术上UCؓ抗原像攻L; <br /> 4、给定哈希|惌扑և能够产生同样的哈希值的两个不同的输入数据,Q这U情늧为碰撞,CollisionQ,q很难,计算上不可行Q在技术上UCؓ抗碰撞攻L; <br /> 5、哈希g表达M关于输入数据的信息?br /> <br />    哈希函数在实际中有多U应用,在信息安全领域中更受到重视。从哈希函数的特性,我们不难惌Q我们可以在某些场合下,让哈希值来“代表”信息本n。例如,验哈希值是否发生改变,借以判断信息本n是否发生了改变。` <br /> <br /> <strong>怎样构徏数字{֐</strong><br />    好了Q有了Hash函数Q我们可以来构徏真正实用的数字签名了?<br />    发信者在发信前用哈希算法求出待发信息的数字摘要Q然后用U钥对这个数字摘要,而不是待发信息本w,q行加密而Ş成一D信息,q段信息UCؓ数字{֐。发信时这个数字签名信息附在待发信息后面,一起发送过厅R收信者收C息后Q一斚w用发信者的公钥Ҏ字签名解密,得到一个摘要HQ另一斚w把收到的信息本n用哈希算法求出另一个摘要H’Q再把H和H’相比较,看看两者是否相同。根据哈希函数的Ҏ,我们可以让简短的摘要?#8220;代表”信息本nQ如果两个摘要H和H’完全W合Q证明信息是完整的;如果不符合,p明信息被人篡改了?<br />    数字{֐也可以用在非通信Q即ȝ的场合,同样h以上功能和特性?<br />    ׃摘要一般只?28位或160位比特,比信息本w要短许多倍,USB Key或IC卡中的微处理器对摘要q行加密变得很ҎQ数字签名的q程一般在一U钟内即可完成?br /> <img height="360" src="http://www.cfca.com.cn/zhishi/image/wz-002-001.gif" width="480" alt="" /><br /> <strong>哈希函数的安全?/strong><br />    哈希函数的安全性直接关pd数字{֐的安全性,如果哈希函数被攻_数字{֐的有效性就会受到质疑?<br />    目前Q已l发明的Hash函数有多U,如Snefru、N-Hash、LOKI、AR、GOST、MD、SHA{。它们在数学上实现的Ҏ各有不同Q安全性也各有不同。目前比较常用的Hash函数是MD5和SHA-1?MD5哈希函数?12位来处理输入数据Q每一分组又划分ؓ16?2位的子分l。算法的输出??2位分l组成,它们联v来,形成一?28位的固定长度的哈希|卌入数据的摘要。SHA-1哈希函数在MD4的基上增加了数学q算的复杂程度,即SHA=MD4+扩展转换+附加?更好的雪崩效应(哈希gQؓ0的比特和?的比特,其L应该大致相等Q输入数据中一个比特的变化Q将D哈希g一半以上的比特变化Q这叫做雪崩效应)。SHA能够产生160位的哈希倹{对SHAq没有已知的密码dQƈ且由于它产生的哈希g数长于MD5Q所以它能更有效地抵抗穷举攻击(包括生日dQ?br /> <br />    但是QQ何一U算法都有其漏洞和局限性。Q何一个哈希函数都会存在碰撞——即在一些特定情况下Q两个不同的文g或信息会指向同一个数字摘要。在一般情况下Q类似碰撞只能尽可能地减,而不能完全避免。从理论上讲Q没有攻不破的密码。随着密码U学的发展,也许会找到攻破某一U密码算法的途径?br /> <br />    评hHash法的一个最好方法是看敌手找C对碰撞消息所q代h有多高。一般地Q假设攻击者知道Hash法Q攻击者的主要d目标是找CҎ更多对碰撞消息。目前已有一些攻击Hash法和计碰撞消息的Ҏ。在q些Ҏ中,有些是一般的ҎQ可用于dMcd的Hash法Q比?#8220;生日d”Q而另一些是Ҏ的方法,只能用于d某些Ҏ的Hash法Q比如适合于攻d有分l链l构Hash法?#8220;中间盔Rd”Q适用于攻d于模q算的Hash函数?#8220;修正分组d”。坚固的哈希函数可通过设计有效的碰撞处理机Ӟ或增加数字摘要的位数来增加复杂度Q以减少撞出现的概率,<br /> <br />    2004q??7日,在美国召开的国际密码学会议QCrypto’ 2004Q上Q一些国家的密码学者作了破译Hash函数的新q展的报告,其中我国׃大学的王云教授做了破译MD5、HAVAL-128、MD4、和RIPE MD法的报告?br /> <br />    ?005q?月,据王云教授的研I报告,他们已经研究Z搜烦SHA-1撞的一pd新技术。他们的分析表明QSHA-1的碰撞能在小?^69ơHash操作中找到。对完整?0轮SHA-1的攻击,q是W一ơ在于2^80ơHash操作q个理论界限的情况下扑ֈ撞。根据他们的估计Q对于羃减到70轮的SHA-1能够用现在的计算机找?#8220;实碰?#8221;。他们的研究ҎQ能自然地运用到SHA-0和羃减轮数的SHA-1的破译分析上?br /> <br />    2005q??日,Arjen LenstraQ王云QBenne de Weger 宣布Q他们构造出一对基于MD5 Hash函数的X.509证书Q生了相同的签名。他们提Z一U构造X.509证书的方法,在他们所构造出的证书对中,׃使用了MD5法Q签名部分生了撞。因此,当证书发布者用MD5作ؓHash函数Ӟ发布者就会在证书中生相同的{֐Q导致PKI的基原理遭到可信性破坏。这意味着Q从单独某个证书无法定是否存在另一个不同证书有着相同的签名。由于第二个相同{֐证书存在的可能性,证书发布机构无法验证U钥?#8220;拥有证明”Q即无法验证证书中的{֐。因此,使用“ZMD5函数”公钥证书的Q何一斚w无法保所谓的证书拥有者是否真实拥有相应的U钥?br /> <br />    他们也想构造一对基于SHA-1的X.509证书Q生相同的{֐。然而,他们q做不到q一炏V因Z生SHA-1撞q需要相当长一D|间的研究?br /> <br />    专家指出QA.Lenstra和王云{h声称已经成功地构造了两张W合XQ?09证书数据l构Q拥有同L名而内容却不同的证书,但该构造方法对证书的部分域要有Ҏ安排Q签名算法RSA的密钥也是按照特D规律生成的Q要用来d某个实际应用的电子签名系l仍需时日。而对于SHA-1法Q说其从理论上被破解都还为时q早Q只能说其破解工作取得了重大H破Q破解所需要运次数已从原来设计时估算?^80ơ降低ؓ2^69ơ,q比ID法快了2Q4Q倍,?^69ơ运需要6Q0Q年左右的时_在实际计上仍然是不可行的?br /> <br />    除了q算斚w的瓶颈外Q哈希函数的不可逆性决定了d者无法轻易得手,没有人可以保证通过q个发现的每个碰撞都?#8220;可用”的碰撞。在漫长的运后Q你得到的也许包含一些有价值的信息Q也许就是理Z存在的单U碰撞,q算瓉和信息匮乏都会黑客们的U种努力成ؓ徒劳……据业内h士估计,在当前的技术条件下Q2^50或2^ Q0ơ运量的范围内的攻L法才会ؓ我们带来ȝQ即引发实际意义上的d行ؓ。在新研I成果发布前的一D|间内QSHA-1 法只能被称作不完美Q但q是安全的。基于PKI技术进行电子签名的最l用P目前q不用担心自q{֐被伪造或遭遇{֐人抵赖?br /> <br />    另外Q安全专家强调:一U算法被破译Q和整个企业的安全系l被ȝQ是两个不同的概c因为随着d技术和能力的提高,法也会“水涨舚w”Q向前发展进步。王教授所取得的成提醒密码学家研I新的算法,提醒有关标准化机构要提前修改法标准Q也提醒有关CA和电子签名品开发商支持新的法。当Ӟ有些完全Z摘要法的密押系l和电子货币pȝQ还需要尽早考虑替换Ҏ?br /> <br />    国国家技术与标准局QNISTQ曾l发表如下评论:“研究l果说明SHA-1的安全性暂时没有问题,但随着技术的发展Q技术与标准局计划?010q之前逐步淘汰SHA-1Q换用其他更长更安全的算法(如:SHA-224, SHA-256, SHA-384和SHA-512Q来代替?#8221;<br /> <img src ="http://www.tkk7.com/masen/aggbug/180703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/masen/" target="_blank">Masen</a> 2008-02-19 16:15 <a href="http://www.tkk7.com/masen/articles/180703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>定制自己的WebLogic LDAP Authentication Providerhttp://www.tkk7.com/masen/articles/179174.htmlMasenMasenSun, 03 Feb 2008 07:17:00 GMThttp://www.tkk7.com/masen/articles/179174.htmlhttp://www.tkk7.com/masen/comments/179174.htmlhttp://www.tkk7.com/masen/articles/179174.html#Feedback0http://www.tkk7.com/masen/comments/commentRss/179174.htmlhttp://www.tkk7.com/masen/services/trackbacks/179174.html定制自己的WebLogic LDAP Authentication Provider

旉Q?005-04-20
作者:马晓?/a>
览ơ数Q?158322
本文关键字:
文章工具
推荐l朋? src= 推荐l朋?/a>
打印文章 
打印文章
目录

1 J2EE Security ?LDAP Security
2 JAAS和WebLogic Security Framework
3 了解WebLogic LDAP Authentication Provider
4 定制自己的Custom LDAP Authentication Provider
5 部v中的注意事项
6 l束?/a>
7 参考资?/a>

  从WebLogic Server 7.0开始,WebLogic Server的安全机制有了全面的改变Q实C一个更加规范的ZJAAS的Security FrameworkQ以及提供了一pd设计良好的Security Service Provider Interface。这h们可以根据自q具体需求,通过Custom Security Authentication Provider来实现安全上的定制功能?/p>

  本文以WebLogicQWebLogic Server 8.1Q?Security?LDAP为基Q介lCustom LDAP Authentication Provider如何l我们带来更多的灉|性,和系l安全设计上更多的空_以及讨论如何实现一个Custom LDAP Authentication Provider和部|过E中的一些良好经验?/p>

  ׃本文涉及到的范围太广Q不可能一一详细讨论Qؓ了没有相关基础的读者也能够阅读理解本文Q因此我在文章前半部分Q试N过最z扼要的描述Q来使大家对于J2EE SecurityQWebLogic Security Framework以及LDAP {有一个初步的清晰认识Q进而可以开发出自己的LDAP Authentication Provider。因此很多地方做了比较有限的描述或者介l,更多详细的内容可以参考文后附带的参考资料或者文中给出的链接?a id="1" name="1">

1 J2EE Security ?LDAP Security
  Sun J2EE推出以来Q其安全部分的规范就一直倍受x。我们最常见到安全规范的两个斚w分别是Servlet Security ?EJB Security。目前绝大多数的Servlet容器QJ2EE容器都能很好的支持这些安全规范?/p>

  WebLogic Server作ؓ业界领先的J2EE服务器对J2EE Security的支持是非常优秀的。我们这里将l合WebLogic Security和用越来越q泛的LDAP做一个简要的介绍Q这些是设计开发Custom LDAP Authentication Provider的技术基?/p>

1.1 Authentication 和Authorization
  q里需要大家先明确安全上的两个重要名词Q一个是认证QAuthenticationQ,一个是授权QAuthorizationQ。认证是回答q个人是谁的问题Q即完成用户名和密码的匹配校验;授权是回{这个h能做什么的问题。我们讨论的J2EE Security包括Declarative Authorization和Programmatic AuthorizationQ即一个是通过web.xmlQejb-jar.xml{部|描q符中的安全声明通过容器提供的服务来完成权限控制的;一个是通过HttpServletRequest.isUserInRole()和EJBContext.isCallerInRole()q样的编E接口在应用中自己完成权限控制的?/p>

1.2 资源QResourceQ和Security Role
  
资源原本只包?Web Resource和EJB ResourceQ但在WebLogic Security中扩展到几乎M一个WebLogic Platform中的资源Q具体可以参?a target="_blank">http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777。授权就是针对资源的讉K控制?/p>

  J2EE Security是基于Security Role的。我们可以将一l资源与一个Security Roleq行兌来达到控制的目的——只有拥有该Role权限的用h能够讉Kq些资源。简单的_我们可以通过l用户分配不同的Security Role来完成权限的控制。复杂的情况下包括用?用户l,以及Principal和Role的映关pȝ{。下面是一个声明性安全在web applicationQwar包中WEB-INF/web.xmlQ中的示例:

<web-app>
 <security-constraint>
 <web-resource-collection>
  <web-resource-name>Success</web-resource-name>
  <url-pattern>/welcome.jsp</url-pattern>
   <http-method>GET</http-method>
   <http-method>POST</http-method>
 </web-resource-collection>
 <auth-constraint>
  <role-name>webuser</role-name>
 </auth-constraint>
 </security-constraint>
 <login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>default</realm-name>
 </login-config>
 <security-role>
  <role-name>webuser</role-name>
 </security-role>
</web-app>

  只有拥有角色webuser的用h能够讉Kwelcome.jsp面Q否则容器会q回401无权讉K的错误。更多信息请参?a target="_blank">http://e-docs.bea.com/wls/docs81/security/index.html?/p>

  同时我们需要在weblogic.xmlQwar包中WEB-INF/weblogic.xmlQ中对security role和principalq行映射关系的配|:

<weblogic-web-app>
 <security-role-assignment>
  <role-name>PayrollAdmin</role-name>
  <principal-name>Tanya</principal-name>
 </security-role-assignment>
</weblogic-web-app>

  q样拥有Principal “Tanya”的用PPrincipal封装到Subject中,用户和Subject兌Q将会拥有PayrollAdmin的权限?/p>

  注意Q一般情况下Z化设计,本文中将假设security rolexprincipal nameQ如果不配置security-role-assignmentQWebLogic会默认做此假设)。即上例中Principal-name也ؓPayrollAdmin?/em>

1.3 LDAP Security
  
LDAP是轻量目录服务QLightweight Directory Access ProtocolQ。越来越多的应用开始采用LDAP作ؓ后端用户存储。在安全上,LDAP Security是基于ACLQAccess Control ListQ的Q它通过l一个用L分配LDAP 操作资源Q比如对一个子树的查询Q修改等Q来最l完成权限的控制。因此在LDAP中,授权工作是以用户lؓ单位q行的。一个用L一般来说是拥有如下一l属性的LDAP EntryQ?/p>


?-3-1

  其中objectclass可以为groupOfUniqueNames或者groupOfNamesQ它们对应的l成员属性分别是uniquemember和member。如果是动态组Qobjectclass为groupOfURLs。动态组一般应用在成员可以通过某种业务逻辑q算来决定的情况下。比如,l理为ZHANGSAN的全部员工。下面是一个典型的动态组QmemberURL属性定义了哪些entry属于该组Q?/p>


?-3-2

  从图1-3-1中我们可以看出,用户WANTXIAOMINGQZHANGSANQLISI属于lHR Managers。这U组和成员的关系是通过属性uniquemember来决定的。同时LADP Group 支持嵌套Q即一个组可以是另外一个组的成员,比如我们Accounting Managersl分配给HR Managersl作为其成员Q?/p>


?-3-3

  q样表CAccounting Managers中的成员Q同时也是组HR Managers的成员。通过q种层关系可以使权限分配变的更加灵zR?/p>

  下面是一些名词的解释Q希望大家对LDAP有更好的理解Q?br />   a) Objectclass —?LDAP对象c,抽象上的概念cM与一般我们理解的class。根据不同的objectclassQ我们可以判断这个entry是否属于某一个类型。比如我们需要找出LDAP中的全部用户Q?objectclass=person)再比如我们需要查询全部的LDAPl:(objectclass=groupOfUniqueNames)

  b) Entry —?entry可以被称为条目,或者节点,是LDAP中一个基本的存储单元Q可以被看作是一个DN和一l属性的集合?属性可以定义ؓ多值或者单倹{?br />
  c) DN —?Distinguished NameQLDAP中entry的唯一辨别名,一般有如下的Ş式:uid=ZHANGSAN, ou=staff, ou=people, o=examples。LDAP中的entry只有DN是由LDAP Server来保证唯一的?/p>

  d) LDAP Search filter ——用filter对LDAPq行搜烦?Filter一般由 (attribute=value) q样的单元组成,比如Q?&(uid=ZHANGSAN)(objectclass=person)) 表示搜烦用户中,uid为ZHANGSAN的LDAP EntryQ再比如Q?&(|(uid= ZHANGSAN)(uid=LISI))(objectclass=person))Q表C搜索uid为ZHANGSAN, 或者LISI的用P也可以?来表CZQ意一个| 比如(uid=ZHANG*SAN)Q搜索uidg ZHANG开头SANl尾的Entry。更q一步,Ҏ不同的LDAP属性匹配规则,可以有如下的FilterQ?(&Qcreatetimestamp>=20050301000000Q?createtimestamp<=20050302000000))Q表C搜索创建时间在20050301000000?0050302000000之间的entry?br />   Filter?“&” 表示“?#8221;Q?#8220;!”表示“?#8221;Q?#8220;|”表示“?#8221;。根据不同的匚w规则Q我们可以?#8220;=”Q?#8220;~=”Q?#8220;>=”以及“<=”Q更多关于LDAP Filter读者可以参考LDAP相关协议Q?a target="_blank">http://www.ietf.org/rfc/rfc2254.txt?/p>

  e) Base DN —?执行LDAP Search时一般要指定basednQ由于LDAP是树状数据结构,指定basedn后,搜烦从BaseDN开始,我们可以指定Search Scope为:只搜索basednQbaseQ,basedn直接下Qone levelQ,和basedn全部下Qsub tree levelQ?/p>

  下面是一个典型的LDAP Treel构Q右侧显CEntry uid=ZHANGSAN, ou=staff, ou=people, o=examples的属性,该entry代表了一个名字叫张三的用P


?-3-4

2 JAAS和WebLogic Security Framework
  现在来多的h开始了解JAASQ用JAAS。WebLogic Security Framework是ZJAAS的。因此我们需要对此有一个非常准的理解才能够设计开发Custom Authentication Provider?/p>

  下面我们从几个名词入手,了解JAAS?WebLogic Security Framework的关键之处:

2.1 PrincipalQSubject和LoginModule
  
a) Principal
  当用h功验证后Q系l将会生成与该用户关联的各种Principal。我们这里将Principalq行化的设计Q认Z个Principal是用户d帐号和它所属于的组QLDAP GroupQ。这样当用户d成功后,我们会在LDAP中执行搜索,扑և用户属于哪些l,q将q些l的名字Q或者其标识作ؓPrincipalq回。这P当用户在LDAP中属于某一个组Qƈ且这个组的名字对应到 web.xml Q或者ejb-jar.xmlQ中的Security roleQ那么这个用户就可以看作拥有讉Kq个Security Role定义的资源的权限?br />   在WebLogic Security Framework中,q个LDAP Group的名字(PrincipalQ和Security Role的映关p,可以通过一?Role Mapping Provider来实现动态的匚wQ即用户的动态权限控制。比如在q行时根据某一个业务逻辑来决定用h否拥有某一个权限。关于Role Mapping ProviderQ读者可以参考下面链接的内容Q?a target="_blank">http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542?/p>

  b) Subject
  JAAS规定由Subject装用户以及用户认证信息Q其中包括Principals。下面是WebLogic Security Framework中Subject的组成图C:


?-1-1

  q样当用戯图访问一个受限的J2EE资源Ӟ比如一个web URLQ或者一?EJB MethodQ可以在web.xml或者ejb-jar.xml中定义,由Security Role控制Q,WebLogic Security Framework会通过 Authorization Provider查用户当前的Subject中是否包含有是否可以讉K受限资源的Principals。由于Principals和J2EE Security Role在weblogic.xml中定义一个映关p(或者通过其他业务逻辑来确定这U关p)Q因此通过q样的关p,可以最l知道用h否有某一个J2EE Resource的访问权限?/p>

  c) LoginModule
  JAAS LoginModule是一个Authentication Provider必须的组成部分。LoginModule是认证的核心引擎Q它负责对用戯n份进行验证,同时返回与用户兌的PrincipalsQ用L录帐P以及LDAP GroupsQ,然后攑օSubject中,供后l的讉K控制使用?br />   我们在LoginModules中完成LDAP的相兌证,查询操作Q将用户在LDAP中所属于的组搜烦出来Q作证后的结果封装到Subject中返回?/p>

2.2 WebLogic Authentication认证q程
  下面我们了解一下WebLogic的认证过E。以下图片来?a target="_blank">http://e-docs.bea.com/wls/docs81/dvspisec/atn.html 我将其中主要部分q行说明?/p>


?-2-1

  Security Framework在WebLogic Server启动时初始化Authentication ProviderQ?Q。当有认证请求进入时QSecurity Framework首先通过AuthenticationProvider.getLoginModuleConfiguration()来获取一个AppConfigurationEntry对象。通过AppConfigurationEntryQ详?a target="_blank">http://java.sun.com/security/jaas/apidoc/javax/security/auth/login/AppConfigurationEntry.html Q可以初始化一个LoginModule。初始化LoginModule的方法ؓQpublic void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)Q可以看到里面有Subject, CallbackHandler{等重要参数?br />   被实例化的JAAS LoginModule完成用L一pd验证d。验证完成后Q在Q?aQ中principals被Principal Validation Provider{֐Q在Q?6bQ中存放C用户兌的Subject里面。Security Framework最后将拿着该用LSubjectd成其他权限控制等d?a id="3" name="3">

3 了解WebLogic LDAP Authentication Provider
  现在我们有信心了解一下WebLogic LDAP Authentication Provider的工作原理了。这里将以WebLogic提供的iPlanet Authentication Provider的配|ؓ例进行说明。在q里也需要明说明一下,Z方便q行描述Q我们将实际属于LoginModule的行Z一q归l到Provider中。没有单独将两个的行为分开Q目的是ZH出整个完整的过E?/p>

3.1 iPlanet Authentication Provider配置


?-1-1

  从上囑֏以看出我们需要指定LDAP服务器的地址Q端口,q接LDAP使用的PrincipalQ不同于前面讨论的PrincipalQ这个Principal实际是一个连接LDAP的用P也就是一个LDAP 中用户Entry?DNQ它必须要有相关的LDAP 搜烦{权限)和CredentialQ一般来说就是口令)?br />   再看下面关于Users的配|:


?-1-2

3.1.1 User Object Class —?前面已经对objectclassq行q说明,指明LDAP Entry属于哪一c?br /> 3.1.2 User Name Attribute —?用户d帐号在LDAP Entry中的属性,一般ؓUID或者cn
3.1.3 User Base DN —?所有的用户会攄到这个子树下面,因此在Provider中对用户q行的搜索将会从q个basedn开?br /> 3.1.4 User Search Scope —?指定搜烦范围为Basedn的直接一U或者全部下U?br /> 3.1.5 User From Name Filter —?使用q个filter可以搜烦出用户信息。其?u会被用戯入的d帐号替换Q从而查询中LDAP中的用户信息


?-1-3

3.1.6 Group Base DN —?从该Base DN开始搜索用L
3.1.7 Group From Name Filter —?%g会被组的名字替换,通过该filter可以搜烦出符合条件的LDAP Group
3.1.8 Static group name attribute —?l名字的属性,属性cn对应的值就是组的名?/p>


?-1-4

3.1.9 Static Member DN Attribute —?静态成员属性,通过该属性可以判断一个Entry是否属于一个组
3.1.10 Static Group DNs From Member DN Filter —?通过该filter可以扑և用户属于哪些l?br /> 3.1.11 Dynamic Group —?动态组是在q行时根据某U业务逻辑Q来军_成员隶属关系的LDAP Group

3.2 iPlanet Authentication Provider的工作原?/strong>
  从上面配|的介绍中可以看出,后端存储为LDAP的情况下Q在Console中我们需要配|的参数已经清楚的表明它所要完成工作的内容?br />   首先Q它使用配置的User BaseDN和FilterQ来Ҏ用户输入的登录帐可行搜索,扑և存放在LDAP中的用户Entry。如果找C个用P那么Provider׃用该用户的DN和用戯入的口oQ进行验证。验证可以用LDAP Bind和LDAP CompareQ这需要根据不同LDAP的特Ҏq行选择?br />
  A. LDAP Bind —?当前的LDAP Connectionl定C个用戯n份上。这样后l的使用该Connection的LDAP Operation都将以该w䆾q行。LDAP Bind需要两个重要参敎ͼ一个是用户Entry的DNQ一个是该用L口o?/em>

  B. LDAP Compare —?LDAP Compare是一个ؓ兼容X.500的古老操作,它用于检查一个属性值是否包含在指定Entry中的属性里。这h们可以在知道用户password存放在哪个属性的前提下,对该属性进行compare。如果成功,表明口o正确。如果属性gؓ散列后的口oQ绝大多数情况)Q有的LDAP Server支持q样的验证,有的不支持,比如iPlanet LDAP Server 5?/em>

  验证成功后,Provider用Console中关于Groups和Memberships中的配置Q查扄户属于哪些LDAP GroupQ而且׃q些l本w可能会有一些嵌套,因此对于搜烦到的l还需要进行查询。即使用filterQ?(&Quniquemember=uid=ZHANGSAN,ou=staff,ou=people,o=examplesQ?objectclass=groupOfUniqueNames))从Group Base DN开始搜索,返回用h属的W一层次GroupQ然后对于这些返回的lDNQ仍焉要用上面的Filterq行搜烦Quniquemember值替换ؓl的DNQ,扑և嵌套关系Q直到查询完成没有组嵌套为止Q此处需要防止陷入嵌套的循环中,比如Group A 包含了Group BQ?Group B又包含了Group AQ有的LDAP Server可以自动出Q有的需要我们程序来判断Q?br />
  然后用L录的帐号Q用h属组的名字(属性CN的值或其他Q,攑օSubject中。最后调用Principal Validator ProviderQ对Subject中的principalsq行{֐Q来表明该Subject是这个Provider生成的。这样防止其他攻击者伪造Subject以及Principalq行ƺ骗。此处也可以解释Z在不同的WLS Domain间不能够传递SubjectQ我们可以通过讄域信L完成q种Subject的传递。设|域信Q使用的Credential是{֐用的Key?br />
  ?-1-5表明了如何设|WebLogic Domain CredentialQ默认情况下WebLogic Server会在启动的时候随即生成一个CredentialsQ在WLS6.1Ӟq个值就是system用户的口令)Q?/p>

  可以惌如果我们实现自己的Principal Validator ProviderQ让它去一个集中的验证服务器中对Subjectq行{֐Q或者验证SubjectQ这样就可以实现域信任,q而完成ApplicationQEJB TierQ层的SSO?/p>

  通过以上的讨论,我们对于实现自己的LDAP Authentication Provider是不是又增加了一份信心?

4 定制自己的Custom LDAP Authentication Provider

  Z要定制自qAuthentication Provider? ׃WebLogic Server已经提供了很多默认的Authentication Provider在一般情况下我们实没有必要实现自己的Provider。但是面Ҏ些针对安全方面的复杂需求时QWebLogic Server提供的Provider很有可能不满些需求,此时需要我们定制自qProvider?/p>

  在这一章的开头部分中我需要简要讨论关于WebLogic MBean TypesQ以及WebLogic Console扩展{内容,目的在于让读者了解到我们通过WebLogic Console可以完成对Custom Security Provider的配|和部vQ我以WebLogic 提供的Sample Security Provider为示例进行说明。详l的信息可以参考以下的一些资源:

http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106272
http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106241

  上面两个链接描述了如何创建MBean Types以及在控制台上配|Custom Authentication Provider.下面q个链接中专门介l了WebLogic Console的扩展:

/techdoc/2005012102.html

  读者可以从http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp下蝲WLS提供的Sample Security Provider?/p>

4.1 MBean Types和WebLogic Console
  一般情况下Qh们可能更习惯通过WebLogic Console对Security Providerq行配置。这里我简要描q这个过E,以及可以到达的一个效果。限于篇q就不详l讨Z?/p>

  从weblogic.management.security.authentication.Authenticator扩展MBean Types?MBean Types是MBeanQhttp://java.sun.com/products/JavaManagement/wp/Q的工厂Q我们扩展SampleSecurityProviders81 包中的SimpleSampleAuthenticator.xmlQMBean Definition FileQ,增加一个我们自定义的参数LDAP Server IPQ?/p>
<MBeanAttribute
  Name = "LDAPServerIP"
  Type = "java.lang.String"
  Writeable = "true"
  Default = "&quot;127.0.0.1&quot;"
/>

  q样在Provider中我们将通过MbeanMaker(WLS提供)生成的SimpleSampleAuthenticatorMBean中取到这个属性:SimpleSampleAuthenticatorMBean.getLDAPServerIP()。MBean在初始化Provider的时候作为参C入。这h们就可以通过MBean中的参数控制Provider的行为?/p>

  当然q个参数是可以在WebLogic Console中设|的Q通过对MBean Types的扩展,在WebLogic Console上看到的画面如下Q?/p>


?-1-1

  q样我们可以通过Console修改配置参数Q修改的Security Provider参数保存在config.xml中,默认的值将保存在MBean Jar File中)?/p>

4.2 Z定制LDAP Authentication Provider
  
当我们面临越来越复杂的安全方面的业务需求时Q或者面临较高的性能要求Q需要根据目标LDAP做针Ҏ的优化Ӟ或者需要将我们已有的认证,或授权模块集成到WebLogicq_ӞWebLogic提供的现成的Provider往往不能满我们的需求?/p>

4.2.1 复杂的业务需?br />   当系l要求用户不仅仅输入用户名(j_usernameQ,口oQj_passwordQ,q需要输入其他信息,比如d的地点,pȝ的名字,用户的类型等{。如果是采用ZJ2EE Form的验证方式, d信息需要提交到j_security_checkQServlet规范定义由容器负责实现的ServletQ,D我们没法处理更多的信息?/p>

  q个时候,如果能够实现我们自己?Authentication ProviderQ那么我们就可以通过TextInputCallback来获取登录表单中更多的信息了Q进而通过q些信息在Provider中完成符合我们需要的处理?/p>

  比如搜狐的登录页面上需要选择用户的类型:


?-2-1

4.2.2 性能需求或者调?/strong>
  有时Q有的用户会比较困惑ZWebLogic LDAP Security Provider在压力测试中的表C很理惻I用户需要较长时间的{待Q才能够d到系l中。由于这些Provider?WebLogic产品的一部分Q因此缺乏对不同目标LDAP Server的有针对性的优化。这样就使得我们无法充分发挥具体LDAP Server的性能调优?/p>

  比如Q有的LDAP Server支持动态组QLDAP Dynamic GroupQ成员关pLq行时根据ldap serverQbasednQfilter{动态决定的Q,可以使用如下的Filter查询用户属于哪些动态组Q?/p>

  (uniquemember=uid=MAXQ,ou=staff,ou=people,o=examples)

  有的LDAP Server虽然支持动态组Q但是支持的有限Q不能用上面的Filter获取用户属于哪些动态组。在WebLogic iPlanet Authentication Provider的实CQ它先是搜烦出全部的动态组Q然后再遍历q些动态组Q依ơ去LDAP中检查用h否属于一个组Q很明显Q这栯然最大程度的满了不同LDAP Server的要求(从品的角度讲可能是必须的)Q但是与LDAP交互的次数大增,q发用户量一大性能下降的比较明显?/p>

  此时Q如果系l中的LDAP支持上面的Filter或者有更好的搜索方式,那么完全可以通过定制Provider完成Ҏ能的优化?/p>

4.2.3 已有权限控制的集?/strong>
  如果pȝ中已l存在了现成的满需求的认证模块Qƈ且已l很好的工作Q在pȝ转向J2EE架构Qƈ使用WebLogic Server做J2EE容器Ӟ我们可能会更愿意直接在Provider中加入这个认证模块?/p>

  lgQ我只是列D了一些可以驱动我们开发自己Provider的需求,怿在读者实际工作中可能会面临更复杂的情况,开发自qProvider是一个非常好的选择?/p>

4.3 LDAP Authentication Provider实现
  本文之前Z表述的方便没有单独提到LoginModuleQ认为LoginModule的行为就是LDAP Authentication Provider的行为。到了目前的具体实现阶段Q我们必d开Authentication Provider和JAAS LoginModule。最l部|到WebLogic上的实际只是LDAP AuthenticationProvider Implements?/p>

  WebLogic SecurityFramework通过Authentication Provider获取具体的JAAS LoginModule。通过LoginModule完成最l登录的工作。因此我们必d实现一个AuthenticationProvider?/p>

  我们一般通过weblogic.security.spi.AuthenticationProvider 来实现自qAuthenticationProvider。这里介l其中的几个重要ҎQ?/p>

  a) public void initialize(ProviderMBean mbean, SecurityServices services)
初始化一个Provider。通过参数MBean我们可以获取到在WebLogic Console中配|的各项参数。进而初始化我们的ProviderQ然后通过Provider传递到LoginModule中?/p>

  b) public void shutdown()
释放一些与ProviderQLoginModule{相关的资源?/p>

  c) public AppConfigurationEntry getLoginModuleConfiguration()
q个Ҏ非常重要Q通过该方法,WebLogic Security Framework可以获取用于初始化LoginModule的AppConfigurationEntry。AppConfigurationEntry中存放了LoginModule的类名等信息Q比如用如下代码返回一个AppConfigurationEntry:

  return new AppConfigurationEntry(
    "examples.security.providers.authentication.SampleLoginModuleImpl",
    controlFlag,
  options);

  其中LoginModule Name?/p>

?examples.security.providers.authentication.SampleLoginModuleImpl"Q我们通过它就可以实例化一个LoginModuleq过LoginModule.initialize()Ҏq行初始化?/p>

  d) public AppConfigurationEntry getAssertionModuleConfiguration()
该方法将q回一个与Identity Assertion Provider兌的LoginModule。这个Assertion LoginModuleQ将只会验证用户是否存在Q以及如果存在返回用LPrincipals?该方法也比较重要Q需要正实玎ͼ比如我们使用CLIENT-CERTq种WEB认证方式Q该Ҏ׃被调用?/p>

  Provider的实现比较简单,读者可以在http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp下蝲WebLogic提供的SamplesQ查看SampleAuthenticationProviderImpl的代码?/p>

 

4.4 LDAP LoginModule 逻辑程
  实现了Provider后,必须拥有我们自己的LDAP LoginModule。下面是一个简单的用于演示的验证逻辑程图。实际的一个LoginModule׃不同的业务需求,情况可能会复杂得多。这里只是描qC最核心最基本的逻辑Q读者能有一个清晰的思\。后面我以q个程Zq行实现?/p>

4.5 LoginModule代码CZ和讲?br />   q里我将使用Netscape LDAP SDK for java作ؓ开发工具实现LDAP相关的操作,读者可以到http://docs.sun.com/db/doc/816-6402-10 下蝲开发手册,?a target="_blank">http://www.mozilla.org/directory/ 下蝲SDK 包。一般来说还可以通过JNDI来操作LDAPQ我个h认ؓSun LDAP JNDI Provider中关于Connection Pool的实现非怼U。但不管使用哪种SDKQ对LDAP的编E原理上基本都是相同的(因ؓZ同样的LDAP协议Q,不同的可能仅仅是接口Q类的名字而已?/p>

4.5.1 初始化Connection Pool
  Z有效使用宝贵Qƈ且有限的LDAPq接Q必M用连接池。下面的代码初始化了一个LDAPq接池:

/**
*
*/
static ConnectionPool pool;
/** * * @throw LDAPException
*/ public LdapClient()
  throws LDAPException
    {
      pool= new ConnectionPool(
        1, 150, "127.0.0.1", 389, "cn=Directory Manager", "88888888");
}

  Sun JNDI LDAP Service ProviderQJDK1.4Q中可以通过在环境变量中讄具体的参数来启用q接池,辑ֈ复用q接的目的。具体可以参考链接:http://java.sun.com/products/jndi/tutorial/ldap/connect/index.htmlQ下面是CZ代码Q?br />
Hashtable env= new Hashtable();

env.put("com.sun.jndi.ldap.connect.pool", "true");
DirContext ctx= new InitialDirContext( env);

4.5.2 Ҏ用户输入的登录帐P搜烦用户Entry
  下面q个Ҏ实现了从LDAP中搜索用户Entry DN的最单的q程。实际上我们可以在其中实现很多定制的功能。比如允许用户用多于一个的帐号dQ只要这些帐可够通过LDAP Searche最l返回一个唯一的用户DN卛_?/p>
  * @return
   * @throws LDAPException
   */
  public String getUserDN( String uid) throws LDAPException{
    LDAPConnection conn= pool.getConnection();
    try {
      String[] attrs= new String[]
{};
      LDAPSearchResults sr= conn.search("o=examples", 2, "(uid="+ uid +")", attrs, false);
      if ( sr.hasMoreElements()) {
        LDAPEntry entry= sr.next();
        return entry.getDN();
}
    throw new LDAPException("No Such Object:"+ uid,
        LDAPException.NO_SUCH_OBJECT);
    }catch ( LDAPException ex) {
      throw ex;
    }finally {
      try {
        if (conn!= null) pool.close(conn);
      }catch ( Exception ex)
{}
    }
  }

  首先需要从池中获取一个LDAPq接Q然后用LDAPConnection.searchҎq行搜烦。我q里以一个典型的LDAP Search接口Zq行说明Q其他API比如JNDI{,Z同样的LDAP协议Q接口中同样参数的含义都是相同的?/p>
public LDAPSearchResults search(java.lang.String base,
  int scope,
  java.lang.String filter,
  java.lang.String[] attrs,
  boolean attrsOnly)

  1) Base: 表明从该Basedn开始搜索,可以通过MBean获取

  2) Scope: 搜烦的范_

    a) LDAPv2.SCOPE_BASEQ?只搜索basedn指定的entry
    b) LDAPv2.SCOPE_ONE, 在basedn的下一Uentry中搜索,不包括basedn
    c) LDAPv3.SCOPE_SUBQ在basedn的全部下Uentry中搜索,包括basedn

  3) FilterQ过虑条件。比如uid=ZHANGSANQ搜索UID为ZHANGSAN的用P再比如uid=ZHANG*Q搜?ZHANG开头的帐号Q或者uid=Z*SQ搜索以Z开头Sl尾的帐受前面已l介l过q里׃多说了?/p>

  4) attrsQ返回该attrs数组中指定的属性,比如new String[]{“uid”}Q只q回属性uidQ其他属性将会不在结果中q回?一般来说我们会要求开发h员只需要的属性返回,q样避免q回无用的属性,降低|络和Server{方面的资源开销Q而且如果存在一个有较大属性集合的EntryQƈ且你q不使用到这个较大的属性集合。D个实际例子来_比如你的pȝ中拥有很多成员数目超q?万或者更多的LDAP GroupsQƈ且你希望通过LDAP Search扑և某一个用户属于的lCNQ那么搜索结果只q回l的CN已经可以满你的要求了,q时没有必要将全部member属性也q回了。这在后面我会有代码来说明?/p>

  q里q有一点很重要的细节,怿对读者会有帮助。比如,你希望搜索到modifytimestamp{Operational Attributes。这些属性(LDAP Server自己负责l护的,用户无法修改的)必须要在参数attrs中指定,LDAP Server才会q回l客L。如果用户希望返回全部User Attributes的同Ӟq回指定?Operational Attributes那该怎么办?不在attrs列表中的属性将不会被返回,一旦我指定了attrsQ是否需要将全部的属性都列在attrs参数中?实际上此时只需要传?new String[]{ “*”, “createtimestamp”}q样的参数即可;“*”号即代表了全部的User Attributes。在Sun JNDI LDAP Service Provider中也是这P管NSun关于JNDI的文档也找不到这L说明。LDAP协议Ҏ的说明在 http://www.ietf.org/rfc/rfc2251.txt W?8c?/p>

  5) attrsOnlyQ只q回属性名Uͼ不包含倹{我们一般设|ؓfalse?/p>

  我们下面看一下Sun JNDI中关于LDAP Search的接口:

public NamingEnumeration search(String name,
  String filter,
  SearchControls cons)
  throws NamingException
name参数是上面的basednQSearchControls中封装更多的讄Q比如:SearchControls. setSearchScope(int scope)其中的scope对应上面?)Q?SearchControls.setReturningAttributes(String[] attrs)Q设|指定返回的属性,对应上面?)?/p>

  其他的参数还包括Size LimitQ即限制搜烦l果的数目(LDAPq回的Entry一般情况下是没有排序的Q除非用一些Sort ControlQ,当你的搜索可能会q回成千上万的EntryӞ限制搜烦的数目是非常明智也是必须的。关于这些,读者可以参考API文档或者LDAP协议?/p>

4.5.3 Ҏ用户的Entry DNQ和用户输入的口令完成n份验?br />   下面的方法实C到LDAP的n份验证。LDAP中的用户实际上就是一个LDAP EntryQ一ơ验证实际是Connection与这个Entryq行l定Q因此验证时我们需要两个必ȝ参数Q一个是Entry的DNQ一个是口o。LDAP的n份验证方式有多种Q包括SASL以及Z证书的验证等Q我们这里只介绍Simple Authentication。关于SASL更多信息读者可以参考:http://www.ietf.org/rfc/rfc2222.txt?/p>
  /**
   *
   * @param dn
   * @param pwd
   * @return
   * @throws LDAPException
   */
  public boolean authentication( String dn, String pwd) throws LDAPException {
    LDAPConnection conn= pool.getConnection();
    try {
      conn.authenticate( dn, pwd);
      return true;
    }catch ( LDAPException ex) {
      if ( ex.getLDAPResultCode()== LDAPException.INVALID_CREDENTIALS) {
        return false; // 用户口o错误 }
      if ( ex.getLDAPResultCode()== LDAPException.NO_SUCH_OBJECT) {
        return false; // 用户不存?}
      throw ex;
    }finally {
      try {
        if ( conn!= null) pool.close(conn);
      }catch ( Exception ex) {
      }
    }
  }


  我们q里使用 LDAP Bind操作完成对用Lw䆾验证。本文前面曾提到也可以用LDAP CompareQ通过比较口o属性(userpasswordQ中的值来完成验证。我们需要根据不同的LDAP Server选择合适的验证Ҏ?/p>

  比如iPlanet LDAP Server 5Q只能通过Bind完成认证Q而Oracle Internet Directory可以通过LDAP Compare完成验证Q而且q支持口令策略?/p>

  如果我们使用了连接池Q连接池中的q接一般都是用权限较大的用户初始化的Q这栯些连接才可以完成对LDAP的搜索操作;而当通过q些q接Ҏ通用戯行n份验证时Q如果通过验证Q连接的w䆾被改变为普通的用户Q或UCؓ与普通用Lw䆾兌Q。普通用户很可能没有除了bind以外的Q何权限,所以在q接被放入池中前Q我们必要恢复q接的n份?/p>

  q样我们必须执行两次LDAP BindQ一ơ用于对普通用户验证n份;一ơ用于恢复连接的较大权限的用戯n份。我们看到这h率是比较低的Q可能你在LDAP Server端统计有2万次bindhQ实际上只有1万hơ登录?/p>

  对于特定的LDAP ServerQ比?Oracle Internet DirectoryQ可以通过LDAP Compare对用戯n份进行验证,q且不会改变q接兌的用戯n份。这h在用池的情况下只需要一ơLDAP Compare卛_。效率有很大提高。如果不通过定制LDAP Authentication ProviderQ这L调优是没法实现的?/p>

  Netscape LDAP SDK的ConnectionPool的实CQ在q接攑օ池中前会查连接的w䆾Q如果n份被改变Q那么会重新q行bind。所以我们没有必要在代码中再做bind?/p>

4.5.4 Ҏ用户的DN搜烦用户属于的组列表
  
有了上面的基后,q个Ҏ很Ҏ理解了。下面我们来看看如何q回用户所属的l?/p>

 

/**
   *
   * @param groupbasedn
   * @param memberDn
   * @return
   * @throws LDAPException
   */
  public List getGroupMembership( String groupbasedn, String memberDn) throws LDAPException {
    LDAPConnection conn= pool.getConnection();
    try {
      LDAPSearchResults sr= conn.search(
          groupbasedn,
          2,
          "(uniquemember="+ memberDn +")",
          new String[] {"cn"},
          false);
      List groups= new java.util.ArrayList();
      while ( sr.hasMoreElements()) {
        LDAPEntry entry= sr.next();
        LDAPAttribute attr= entry.getAttribute("cn");
        if ( attr!= null) {
          String[] values= attr.getStringValueArray();
          if ( values!= null && values.length>0) groups.add( values[0]);
        }
      }
      return groups;
    }catch ( LDAPException ex) {
      throw ex;
    }finally {
      try {
        if ( conn!= null) pool.close(conn);
      }catch ( Exception ex) {
      }
    }
  }

  成员和组的membership主要是通过uniquemember或?member属性来定义的。成员不仅仅可以使用用户Q也可以是组Q因为组可以嵌套?br />
  a) 上面的方法中只实C一个层ơ的搜烦Q即用户——组的搜索,而组间的嵌套搜烦没有实现。读者可以根据系l内的具体情况,在此处也可以做一些优化?/p>

  b) l成员的数量可能比较大,Z避免不必要的开销Q我们指定只q回l的cn属性?/p>

  c) 动态组的优化。在WebLogic默认的实CQProviderQ实际ؓLoginModuleQ会不断的拿动态组中定义的URL中的filter和用LDN去LDAP做搜索,来看用户是否属于该组。本文前面也讨论了,q样效率很低Q完全可以放在Provider本地实现URL和Entry的匹配?/p>

4.5.5 LoginModule中的login()Ҏ实现
  
一切准备就l后Q我们就可以完成LoginModule.login()q个最核心的方法了。下面我Ҏ代码中的注释逐条说明?/p>
  // A
  boolean loginSucceeded;
  // B
  List principals= new java.util.ArrayList();
  /**
   *
   * @return
   * @throws LoginException
   */
  public boolean login() throws LoginException {
    // C
    Callback[] callbacks= new Callback[] {
        new NameCallback("username: "),
        new PasswordCallback("password: ",false)};
    try {
      callbackHandler.handle( callbacks);
    }catch (IOException e) {
      throw new LoginException(e.toString());
    }catch (UnsupportedCallbackException e) {
      throw new LoginException(e.toString() + " " +e.getCallback().toString());     }
    //
    String userName = ((NameCallback)callbacks[0]).getName();
    if ( userName== null || userName.length()== 0) {
      throw new LoginException("User login name is empty!");
    }
    //
    PasswordCallback passwordCallback= (PasswordCallback)callbacks[1];     char[] password = passwordCallback.getPassword();
    passwordCallback.clearPassword();
    if ( password== null || password.length== 0) {
      throw new LoginException("User password is empty!");
    }
    try {
      // D
      String dn= this.getUserDN( userName);
      if ( dn== null) {
        throw new LoginException("User "+ userName +" doesn't exist.");
      }
      // E
      boolean authResult= this.authentication( dn, String.valueOf( password));       if ( authResult== false) {
        throw new FailedLoginException("User login failed.");
      }
      // F
      principals.add( new WLSUserImpl( userName));
      // G
      List groups= this.getGroupMembership( "ou=groups,o=examples", dn);       for ( int i=0, n=groups.size(); i<n; i++) {
        String cn= String.valueOf(groups.get(i));
        // H
        principals.add( new WLSGroupImpl(cn));
      }
    }catch ( LDAPException ex) {
      java.io.StringWriter sw = new java.io.StringWriter();  
     ex.printStackTrace(new java.io.PrintWriter(sw));
      sw.flush();
      throw new LoginException( sw.toString());
    } 
       return loginSucceeded= true;
  }

  a) 用于保存验证l果Q在后箋ҎQcommit{)中?br />
  b) 用于保存和用户关联的PrincipalQ在后箋ҎQcommit{)中?br />
  c) 在JAAS中通过CallbackHandler来完成用户和底层安全认证pȝ间信息的交换Q这里我们通过CallbackHandler获取用户输入的登录帐号和口o。CallbackHandler在初始化LoginModule时由WebLogic Security Framework传入。读者可能会问,我是否可以在此要求WebLogic Security Framework传入我自己实现的CallbackHandlerQ进而获取更多的信息Q支持更多的CallbackQ很遗憾Q不可以。只有在一U情况下Q本文前面也提到Q有一个变通,是在Web应用中,通过Form Basedq种认证方式q行用户w䆾验证Ӟ可以获取更多的登录表单中提交的cgi变量?/p>

  下面的代码将演示q种技术:

    Callback[] callbacks= new Callback[] {
        new NameCallback("username: "),
        new PasswordCallback("password: ",false),
        new TextInputCallback("my_hidden_field")};
    try {
      callbackHandler.handle( callbacks);
    }catch (IOException e) {
      throw new LoginException(e.toString());
    }catch (UnsupportedCallbackException e) {
      throw new LoginException(e.toString() + " " +e.getCallback().toString());     }
    String value= ((TextInputCallback)callbacks[2]).getText();

  q样我们通过((TextInputCallback)callbacks[2]).getText()来获取表单中更多的信息。其中TextInputCallback的Prompt必须要和表单中对应域的名字一_否则取不C息。如果有多个域,则需要传入多个TextInputCallback?/p>

  同时如果d表单中没有这些对应TextInputCallback的域Q或者用JNDI方式验证Q那么会抛出UnsupportedCallbackException?/p>

  下面是一个符合Servlet安全规范的登录表单,其中域my_hidden_field的值将通过CallbackHandler传递到的TextInputCallback中:

<html>
  <form method=”POST” action=”j_security_check”>
    <input type=”text” name=”j_username”>
    <input type=”password” name=”j_password”>
    <input type=”hidden” name=”my_hidden_field”value=”Hello Callback!”>
  </form>
</html>

  d) 生成一个用于存放Principal的集合对?br />   e) 通过d帐号获取LDAP中的Entry DN
  f) 通过DN和用户口令进行n份验?br />   g) 通过验证的用户名加入到Principal列表中,q些以后都将代表用户的一定权?br />   h) 查找用户属于哪些l?br />   i) 这些组的名字加入到Principal列表?/p>

4.5.6 LoginModule中的commit()和abort()Ҏ实现
  
完成了以上的验证后,我们需要通过LoginModule.commit()Ҏ提交验证l果Q或者abort()Ҏ取消验证l果?/p>
  // A
  private Subject subject;
    /**
   *
   * @return
   * @throws LoginException
   */
  public boolean commit() throws LoginException { if (loginSucceeded) {
  // B
      subject.getPrincipals().addAll(principals);
      return true;
    }else {
      return false;
    }
  }
    /**
   *
   * @return
   * @throws LoginException
   */
    public boolean abort() throws LoginException {
    // C
    subject.getPrincipals().removeAll(principals);
    return true;
  }    

 

  我这里仍焉过代码中的注释q行相应的说明:

  a) JAAS SubjectQ在LoginModule初始化时Q由WebLogic Security Framework负责传入Q用L权限信息QPrincipals{)都将装到该Subject中。同时Principal Validator会对Subject中的Principalsq行{֐Q防止被黑客U改?/p>

  b) 用户认证成功的情况下Q将用户兌的Principals加入到Subject中。这L户在q行后箋的访问时QWebLogic Security Framework会检查与用户兌的Subject中是否有可以讉K受限资源的Principal?/p>

  c) 用户dp|的情况下Q有可能是其他的LoginModuleD的整体登录失败,具体参考JAAS Control FlagQ,我们在commit中添加的Principals必须从Subject中删除?/p>


  通过上面的描q和讨论Q相信读者已l对如何实现一个LDAP AuthenticationProvider/LoginModule有了比较清楚的了解。限于篇q省略了很多l节Q对此有需要的读者可以根据文中给出的相关链接做进一步的了解Q或者与我交。从q里我们也可以看到核心的逻辑q是比较单的。其中将LDAP Group作ؓ用户的PrincipalQ是J2EE Security与LDAP Securityl合的非帔R要的一步。通过q种l合Q我们可以将LDAP作ؓ后端Q设计出ZJ2EEQJAAS的用戯n份验证,权限理{系l?a id="5" name="5">

5 部v中的注意事项
  开发完成LDAP Authentication Provider后,需要将通过WebLogic MBean Maker生成的MBean Jar File攑ֈ${WL_HOME}/server/lib/mbeantypes目录下。由于WebLogic Server在启动时会加载这些ProviderQ因此在一个分布式的环境中QWebLogic Domain内的全部WLServerQ包括Managed ServerQ均需要部|该Jar包?/p>

  同时׃使用了Provider MBeanQ因此,当你删除了MBean Types中定义的Qƈ且通过修改保存在config.xmlQWebLogic Domain配置文gQ中的属性时Q重新部|的Jar包会DWebLogic Server无法正常启动Q因为它没有办法按照config.xml中配|的属性初始化MBean。此旉要手工修改config.xmlQ删除相关的属性即可?/p>

<examples.security.providers.authentication.simple.SimpleSampleAuthenticator
  ControlFlag="OPTIONAL"
  Name="Security:Name=myrealmSimpleSampleAuthenticator"
  UserBaseDN="ou=people, o=examples" Realm="Security:Name=myrealm"/>

  比如UserBaseDN已经不需要通过MBean来管理,q且在MBean Types中已l删除,那么直接删除q里的UserBaseDN="ou=people, o=examples" 可以了?a id="6" name="6">

6 l束?/strong>
  本文所讨论的内Ҏ些过于广泛,从我个h角度Ԍ写v来也比较困难Q很多技术问题没有进行深入的讨论。其实只是希望通过本文Q能够帮助读者对WebLogic SecurityQJ2EE SecurityQLDAP Security以及JAAS有一个初步的认识Q能够给希望实现LDAP Authentication ProviderQ或者被WebLogic LDAP Authentication Provider困惑的读者一些启C。只要能够对读者有所帮助Q本文的目的q辑ֈ了?/p>

  限于幅Q本文很多地方的表述可能q不清晰Q也可能会有一些错误,如果在阅读过E中产生M疑问或者有M看法都可以通过E-mail来与我交,我也非常希望能和大家交流?a id="7" name="7">

7 参考资?/strong>
a) http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777 关于WebLogic Resource的更多说?br /> b) http://e-docs.bea.com/wls/docs81/security/index.html 关于WebLogic Security的全部内?br /> c) http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542 关于Role Mapping Provider的更多内?br /> d) http://dev2dev.bea.com.cn/techdoc/2005012102.html 关于WebLogic Console的扩?br /> e) http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp Custom Seuciryt Provider的Samples
f) http://java.sun.com/products/JavaManagement/wp/ 更多关于Sun JMX
g) http://java.sun.com/products/jndi/tutorial/ldap/connect/index.html 关于如何使用Sun JNDI LDAP Service Provider中提供的q接?br /> h) http://www.ietf.org/rfc/rfc2254.txt 更多关于LDAP Filter
i) http://www.ietf.org/rfc/rfc2251.txt LDAP V3协议
j) http://www.ietf.org/rfc/rfc2222.txt 更多关于SASL



Masen 2008-02-03 15:17 发表评论
]]>
վ֩ģ壺 ҹƷþþþþ| ҹһ| þþþþþþþùƷ| þԭavapp| µĻ| Ƭ쿴| 㽶Ƶ| Ů˱ŮˬƵ| һƵѹۿ| ɫͼ߹ۿ| һƵ| Ļһ| պav| պ˳ۺձ| žƷƵվ| ľƷ| ŷձ| ޹ƷȾþ| ޵һ߹ۿ| 91ѾƷԲ߲| ޾ƷƵѹۿ| ASS츾ëPICS| ޹һ| 뿴avվ| պҹTVӰԺ| ҹaëƬƵ| a߹ۿ| ɫͼ| ۺϽ߹ۿ| ˻18س˻18Ƶ | ˿Ƶ| ۺƵ| av߾Ʒ޵һվ| ޾Ʒרþþ | ѹԺ߹ۿ| ֳִִƵ | **ʵһëƬaa| yellowվ| ѸԴվƵ| Ļþ2020| 91ƷˬžӰԺ|