﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-beauty_beast-随笔分类-AppServer</title><link>http://www.blogjava.net/beauty_beast/category/7389.html</link><description>上善若水   厚德载物 
</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 12:19:03 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 12:19:03 GMT</pubDate><ttl>60</ttl><item><title>(转载)WebLogic Server 之安全(架构篇) </title><link>http://www.blogjava.net/beauty_beast/archive/2006/10/11/74500.html</link><dc:creator>柳随风</dc:creator><author>柳随风</author><pubDate>Wed, 11 Oct 2006 02:50:00 GMT</pubDate><guid>http://www.blogjava.net/beauty_beast/archive/2006/10/11/74500.html</guid><wfw:comment>http://www.blogjava.net/beauty_beast/comments/74500.html</wfw:comment><comments>http://www.blogjava.net/beauty_beast/archive/2006/10/11/74500.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/beauty_beast/comments/commentRss/74500.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/beauty_beast/services/trackbacks/74500.html</trackback:ping><description><![CDATA[
		<p align="center">WebLogic Server 之安全(架构篇) <br /></p>本文来自<a href="http://www.chinaitpower.com/2005Nov/2005-11-23/209281.html">http://www.chinaitpower.com/2005Nov/2005-11-23/209281.html</a><br />安全的变化可是WebLogic Server近期版本(7.0,8.1)最大的变化之一，全新的安全体系架构，既易于客户化安全解决方案的变化，又能实现细节和应用基础架构分离，使安全更易于部署，管理，维护和根据需求变化.同时也是支持最新的J2EE的标准和规范。本文试图从架构方面详细讲解WebLogic Server 7.0的安全体系及其相关细节，希望读者能对其机制有个框架层次和深度上的了解，文档参照了WebLogic Server 7.0的英文版手册，表述不当的地方，希望大家一起探讨。BTW,新出的WebLogic Server 8.1的安全架构完全和WebLogic Server 7.0一致。<b><br /><br />WebLogic Security的基本特征</b><p>开放，灵活的架构。<br />1. 基于标准，易于理解的设计<br />2. 基于WebLogic Server应用的端对端的安全（从主机到浏览器）<br />3. 能与已存在的安全规则(Scheme)进行集成，从而有利于保护企业投资。<br />4. 安全的工具集成到一个灵活，统一架构的系统以便于在企业级进行安全管理<br />5. 通过将公司的业务规则和安全policies的对应，能方便实现根据业务需求客户化应用安全<br />6. 易于Security Policy的更新。<br />7. 易于客户化安全解决方案的变化。<br />8. 因为具有模块化的架构，因此安全架构能根据不同公司的特殊需求不断变化<br />9. 能够配置多个Security Provider<br />10. 安全实现细节和应用基础架构分离，使安全更易于部署，管理，维护和根据需求变化<br />11. 当前的WebLogic Security providers提供了和应用无关的可行的安全规则。（Scheme）<br />12. 使用WebLogic custom security provider进行客户化工作<br />13. 通过管理控制台进行统一的安全规则(Rule),Security Policy和Security Provider的统一管理<br />14. 对最新的J2EE安全技术的支持，包括JAAS(Java Authentication and Authorization Service),JSSE(java Secure Sockets Extensions),and JCE(java Cryptography Extensions)<br /></p><br /><table cellspacing="2" cellpadding="2" width="95%" border="0"><tbody><tr><td bgcolor="#cccccc"><b>易于使用：</b>对最终用户而言，只需要登陆一次，就能访问所有资源（SSO），（限制在WebLogic Domain）<br /><b>可管理：</b>提供WebLogic security Provider,当前的Provider支持所有需要的安全功能，Security Data存放在WebLogic Server提供的LDAP Server中<br /><b>客户化:</b> WebLogic Security API,JAAS,JSSE custom security provider through SSPIs(WebLogic Server Security Service Provider Interfaces)</td></tr></tbody></table><b><br />WebLogic Server Security从6.x到7.0的变化: <br /><br /></b><table cellspacing="1" cellpadding="2" width="95%" border="1"><tbody><tr><td bgcolor="#c0c0c0">WebLogic Server 6.x </td><td bgcolor="#c0c0c0">WebLogic Server70 </td></tr><tr><td>安全的API </td><td>非常多的已存在API不推荐使用，BEA建议使用基于J2EE规范的标准接口实现应用 </td></tr><tr><td>JAAS认证 </td><td>JAAS认证增强已提供对于IIOP和t3客户端访问的LoginModules </td></tr><tr><td>审计 </td><td>无需实现WebLogic.security.Audit接口已在你的应用中加入审计功能，WebLogic Auditing provider已提供。 </td></tr><tr><td>定义weblogic.xml和weblogic-ejb-jar.xml和weblogic-ra.xml中的安全需求 </td><td>增强可以通过管理控制台定制 </td></tr><tr><td>系统密码 </td><td>没有指定的系统账号 </td></tr><tr><td>ACL </td><td>ACL已经不推荐使用，并由security policy代替 </td></tr><tr><td>用户和组 </td><td>仍然存在，并通过security policy将weblogic resouce赋予user,group或security role </td></tr><tr><td>6.x安全Realm </td><td>不推荐使用，但仍存在Realm Adapter当你进行安全的转换时 </td></tr><tr><td></td><td>支持多个Security Provider </td></tr><tr><td>SSL </td><td>支持JSSE标准和TLS v1(Transport Layer Security)协议 </td></tr><tr><td></td><td>支持J2EE JKS keystores </td></tr></tbody></table><br /><b>安全基础</b><br />Audit(审计):<br />提供对操作请求和请求输出的搜集，存储和发布，提供了对计算机行为的电子纪录。<br />在Security operations happens的前后，WebLogic调用Auditing Provider对特殊事件（基于audit criteria or serverity level）进行纪录同时写入LDAP Server,数据库或文件系统中。 
<p>Authentication(认证)<br />通过使用username/password组合来判断用户是否是一个系统的合法用户，主要回答"Who Are You?"的问题。<br /></p><b>1. Principal &amp; Subject<br /></b><table cellspacing="1" cellpadding="2" width="95%" border="1"><tbody><tr><td>Principal认证通过后赋予用户或组的身份（Identity）Subject JAAS 要求通过subject，Container知道客户信息，包括pricipals</td></tr><tr><td>·Relationships Among Users, Groups, Principals and Subjects</td></tr></tbody></table><br /><span lang="EN-US"><img height="329" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image001.gif" width="410" v:shapes="_x0000_i1027" /></span><br /><br />成功的认证后，principal签名同时存在一个subject中，principal validation provider签名，并且Authentication Provider的LoginModule存放pricipal到subject中，当caller试图访问存在subject中的pricipal时，principal validation provider认证pricipal自签名后没有改变，然后principal回给caller. 
<p>2. JAAS<br />JAAS实现了java版本的PAM模块，允许应用和后层采用的认证技术，同时允许再不更改应用的情况下采用更新的或者变化了的安全认证技术。<br />WebLogic sever使用JAAS在胖客户端认证和内部认证,因此，只有客户化Authentication Provider的开发者和远程胖客户端需要直接使用JAAS, Thin 客户端和在container内的胖客户端(EJB throgh servelt)不需要直接使用JAAS.</p><p>·JAAS LoginModules<br />认证用户，发布Subject.不用于perimeter authentication</p><p>·JAAS Control Flags<br />Multiple Authentication provider configured<br />REQUIRED : 必须成功，如果失败，authentication继续其他的LoginModule(当前)<br />REQUISITE:必须成功，成功继续，不成功，将返回应用。<br />SUFFICIENT：不需要成功，如果成功，返回应用，如果失败，继续LoginModule List<br />OPTIONAL:允许成功或失败，但必须通过一个LoginModule</p><p>·CallBackHandlers<br />CallbackHandler 是为将参数或复杂对象传入方法中的一种高度灵活的规范。包括NameCallback:return username<br />PasswordCallback:return password<br />TextInputCallback: return 用户在Login form中输入的数据。<br />Security service将不同的Callback传入CallbackHandler要求获取不同类型的信息，CallbackHandler的实现决定如何根据不同的Callback获取和显示。</p><p>·Mutual Authentication<br />客户端和服务端都需要相互认证，WebLogic Server支持2-way SSL</p><p>·Identity Assertion Providers and LoginModules<br />当和LoginModules一起使用时，Identity Assertion provider支持Sigle-sign-on,LoginModule包含：<br /></p>→ 客户化开发的Authentication Provider<br />→ BEA 开发的WebLogic Authentication Provider<br />→ 第三方的Authentication Provider<br /><b><br /></b>·Identity Assertion and Tokens<br />Identity Assertion Provider支持用户名的mapping,对应到weblogic server中的一个正确的token,能够开发Identitty Assertion Provider已支持多种token类型，但 只有一种为Active状态，同时只有一种Identity Assertion provider最终执行这一mapping的操作。 <br /><br />Authentication类型<br />WebLogic authentication provider支持的认证类型有：<br /><table cellspacing="1" cellpadding="2" width="95%" border="1"><tbody><tr><td>用户名/口令认证:SSL,HTTPS<br />CA(Certification Authentication):当SSL或HTTPS客户请求发起时，WebLogic Server回应客户一个数字认证（digital certification），客户证实这个数字认证从而建立SSL联结，数字认证是由仍证机构CA发放。<br />Perimeter Authentication<br />主要用于应用服务器外的远程用户的认证过程<br />远程用户指定assert identity和相应得材料来完成。<br />Authentication agent能采用各种格式，VPN,Firewall,企业级认证服务。<br />Authentication agent处理认证过程同时导致artifact和token产生。Identity assertion的概联使weblogic server能使用perimeter authentication schemes提供的认证机制，比如Checkpoint's OPSEC,the emerging Security Assertion Markup Language(SAML), enhancement of Common Secure Interoperability(CSI)v2完成功能。</td></tr></tbody></table><span lang="EN-US"><br /><img height="249" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image002.gif" width="554" v:shapes="_x0000_i1028" /></span><br /><br />3. Authorization<br /><br />Authorization在用户和weblogic 控制的资源中交互的进程，基于用户身份或其他信息。<br />Authorization主要回答的问题是"What can you access" 
<p>WebLogic resource:包含<br />管理资源<br />应用资源<br />COM资源<br />EIS资源<br />EJB<br />JDBC<br />JMS<br />JNDI<br />Server资源<br />URL资源<br />Web Service资源</p><p>·Security Policy<br />代替ACL,主要回答的问题是：Who have access to WebLogic resource?<br />当你将weblogic resource和用户，组，security role相关联时，Security Policy产生。<br />Security Policy存储在Authorization Provider的数据库中，当前的WebLogic Authorization provider存在LDAP Server中<br />BEA推荐在security role上建立security policy.</p><p>·ContextHandlers<br />从resource container中获取context和有关container的信息，为security provider做出访问或权限mapping决定的提供信息。<br /></p>·Access Decisions<br />回答了"is access allowed?"的问题.Access Decision结果为PERMIT,DENY,ABSTAIN 
<p>·Adjudication<br />解决了当多个Authorization Provider提供授权时产生的冲突问题。Adjudication Server权衡多个Access Decision的结果，并且决定最终permit或者deny的结果。当唯一的Authorization provider返回结果为ABSTAIN时，Adjudication provider决定怎样处理。</p><p>·SSL<br />当前WebLogic Server支持1-way方式的SSL认证，使用管理控制台，能够配置2-way的SSL.<br />需要配置privite key,包括public key的数字证书(digital certificate)，和至少由一个CA认证的数字证书(digital certificate),有可能需要安装root trusted CA's digital certificate.</p><p>为获取数字证书，自己生成public key,private key和认证签名请求(CSR)(包含public key),同时将CSR发到认证机构已得到签名的证书。<br />Public key存放在WebLogic domain目录下的文件系统中<br />Private key和通过认证的证书能存放在文件系统或通过WebLogic keystore provider提供的keystore中。</p><p>SSL提供<br />1） 应用间沟通的机制便于认证双方的身份<br />2） 应用间交换数据的加密。</p><p>Server authentication:WebLogic Server使用经过CA签名的数字证书，向客户认证。当客户不需要向Server证明自己的数字证书时，这种联接的方式叫1-way SSL认证。<br />Client Identity Verification:可选的，客户必须向WebLogic Server出示数字证书,WebLogic Server证实数字证书由CA发放同时建立SSL联结，这种方式叫2-way SSL认证。<br />Confidentiality所有客户的请求和Server的相应在网络上的传输都加密。<br />数据完整性:客户端和服务端传输的数据受保护。</p><p>SSL Tunneling<br />SSL是在基于IP协议上Tuneled.意味着每个SSL纪录被封装同时有使它在其他协议上发送的header来包装。</p><p>One-way/Two-way SSL认证<br />在One-way情况下，客户端通过两种方式检查数字认证<br />1． 检查数字认证在它的可信任认证机构中(CA)<br />2． 检查认证中的主机名和Server一致。<br /><br /><span lang="EN-US"><img height="292" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image003.gif" width="553" v:shapes="_x0000_i1029" /></span></p>Domestic SSL和Exportable SSL <br /><table cellspacing="1" cellpadding="2" width="95%" border="1"><tbody><tr><td>Exportable SSL支持512位证书和40-或50-位数据加密（标准支持）<br />Domestic SSL支持768位和 1024位证书和128位数据加密(Ask BEA Sales)</td></tr></tbody></table><br /><b>Security Realm（安全域） </b><br />Security Realm 包含整套保护WebLogic资源的机制，由一系列配置好的Security Provider,用户，组，security role,和security policy构成<br /><br /><span lang="EN-US"><img height="237" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image004.gif" width="554" v:shapes="_x0000_i1025" /></span><br /><br />·用户 <br />当用户访问WebLogic Server时，通过JAAS LoginModule到认证提供者，如果通过认证，weblogic server通过一个线程将principal和用户相联，在用户线程开始执行代码前，WebLogic Server检查WebLogic resource的policy和principal以确认用户取得相应的要求的权限。 
<p>当定义一个用户时，WebLogic 对用户的密码进行加密，同时当WebLogic接收到客户请求时，客户端请求的密码被hash加密,同时和以存在的加密密码比较以判断是否符合。</p>·组<br />组是用户的逻辑组合。在一个Security Realm中，所有的用户和组必须唯一。 
<p>·Security Role<br />动态赋予用户和组，根据用户名，组的成员或一天中的某个时间。 <br />能在一个WebLogic server的域中，针对单个应用定义资源。</p><p>在weblogic 6.x中，security role只能赋予web application 和 EJB, 在 70版本中，用户的security role能扩展到所有的WebLogic资源。</p>·Security Policy <br />Policy是WebLogic资源和用户，组，和Security Role的关联。用以保护WebLogic资源不收不被授权的用户访问。它代替了6.x中用的ACL 
<p>·Security Provider<br />对应用提供安全服务的模块，用以保护WebLogic资源，客户能够直接使用WebLogic Server直接提供的security provider,也能购买第三方的产品进行集成，或者开发自己的security provider.</p><p>Security Provider数据库<br />Security Provider数据库包含用户，组，role和policy,以及有些安全提供者使用的密码。比如Authentication provider要求用户和组的信息。Authorization provider要求安全policy方面的信息，Role Mapping Provider要求安全Role方面的信息，Credential Mapping provider要求resource Adapter用于联结后台的EIS系统的密码信息。这些信息都存放在数据库中。</p><p>Security provider的数据库在递一次使用security provider时候被初始化，所做的工作是<br />当WebLogic实例重起。 <br />当一个security provider的Mbean被调用。</p><p>当你在一个security realm中配置了多个security provider,那么这些security provider将是用同一个security provider数据库。当你在不同的security realm中配置两个security provider或两个相同的security provider,他们将使用不同的provider database.,在一个时间只有一个security Realm处在激活状态。<br /><br />·Embedded LDAP Server<br />Embedded LDAP Server被当前的WebLogic security provider使用作为数据库存储用户，组，role和policy. 是一个完整的LDAP Server,支持的操作有：<br />在LDAP Server中访问和修改<br />使用LDAP browser导入或导出安全的数据<br />被WebLogic security provider读写访问。<br />WebLogic Server不支持在Embedded LDAP Server中加入属性(Attribute)<br /></p><table cellspacing="2" cellpadding="2" width="95%" border="1"><tbody><tr><td bgcolor="#c0c0c0">WebLogic Security Provider </td><td bgcolor="#c0c0c0">Embedded LDAP Server Usage </td></tr><tr><td>Authentication </td><td>Stores user and group information. </td></tr><tr><td>Identity Assertion </td><td>Stores user and group information. </td></tr><tr><td>Authorization </td><td>Stores security roles and security policies. </td></tr><tr><td>Adjudication </td><td>None. </td></tr><tr><td>Role Mapping </td><td>Supports dynamic role associations by obtaining a computed set of roles granted to a requestor for a given WebLogic resource. </td></tr><tr><td>Auditing </td><td>None. </td></tr><tr><td>Credential Mapping </td><td>Stores Username-Password credential mapping information. </td></tr></tbody></table><p>·Security Provider的类型<br />Authentication Provider<br />主要用于:<br />用户名/密码的认证<br />直接和WebLogic Server基于证书的认证<br />通过外部的web server基于HTTP证书的认证代理</p><p>Identity Assertion Provider主要用于处理基于perimeter的认证和多个安全另牌(token)类型和协议。</p><p>在安全域中必须至少有一个Authentication provider,同时你可以配置多个Authentication Provider,意味着你有多个LoginModules,每个处理不同的认证。管理员配置不同的provider以便于当用户登陆系统时多个LoginModules怎样被调用。<br />Identity Assertion Provider<br />使用客户提供的可能存在于请求外的Token识别客户的身份。<br /></p><p><b>WebLogic Security Service Architecture(架构) <br /><br /></b>Security Framwork <br /><br /><span lang="EN-US"><img height="374" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image005.gif" width="552" v:shapes="_x0000_i1030" /></span><br /><br />security framework提供的应用API被security和应用开发者定义安全服务使用，同时做为weblogic Container(Web和EJB),资源 Container和安全提供者间的中间层。</p><p>下面详细描述各层间的交互<br /></p>·认证过程 <br /><br /><span lang="EN-US"><img height="257" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image007.gif" width="553" v:shapes="_x0000_i1026" /></span><p>当用户通过username/passwd组和登陆系统时，WebLogic建立信任同时对于每个JAAS请求回送包含principal的subject。</p><p>通过认证后，一个authentication context建立。</p><p><br />·Identity Assertion Process<br /><br /><span lang="EN-US"><img height="262" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image009.gif" width="553" v:shapes="_x0000_i1031" /></span><br /></p><p>主要用于perimeter authentication过程，当使用perimeter authentication时，外部系统传入token给Identity Assertion Provider.如果通过，Identity Assertion provider将token对应到相应的username,并将回送username到weblogic server,然后由WebLogic Server将username送回到JAAS的CallbackHandler并传递到每个配置的Authentication Provider的LoginModule模块，最后LoginModule能产生包含正确principal的Subject.<br /><br />·Principal Validation Process <br /><br /><span lang="EN-US"><img height="257" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image010.gif" width="553" v:shapes="_x0000_i1032" /></span><br /><br />WebLogic将subject传递给principal validation Provider,后者将对principal进行签名并通过WebLogic Server把他回送客户的应用。以后principal validation provider将用来证明经过签名的subject中的principal没有被别人改过。</p><br />Authorization process<br /><br /><span lang="EN-US"><img height="236" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image011.gif" width="551" v:shapes="_x0000_i1033" /></span><br /><br />Authenorization process最初由用户或系统要求使用WebLogic Server资源时触发。Resource Container处理被请求的资源。 Resource Container调用WebLogic Security Framwork同时传递给它相应的请求subject和请求resource的信息。WebLogic framework调用配置的role mapping provider,同时将传递的信息转换成相应的格式，由Role Mapping provider回送role的列表给WebLogic security framework, Authorization provider决定subject是否有访问相关资源的权限，如果配有多个Authorization provider,WebLogic security framework决定任何有冲突的访问判断传递给Adjudication Provider,最后由Adjudication Provider决定最终的访问许可。 <br /><p>·Role Mapping Process<br /><br /><span lang="EN-US"><img height="236" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image012.gif" width="551" v:shapes="_x0000_i1034" /></span><br /><br />WebLogic security framework调用每个role mapping provider以得到请求的role list，如果security policy指定请求赋予相应的role,role就被加入到subject可用的role list中，同时role list 返回给WebLogic security framwork做为下一步访问控制的判断。<br /><br />·Auditing Process <br /><br /><span lang="EN-US"><img height="236" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image013.gif" width="551" v:shapes="_x0000_i1035" /></span><br /><br />Authentication Provider除了提供认证服务外，需要发送audit事件，初始化AuditEvent对象（包含audit的event类型和audit的级别）,Authentication Provider随后调用Audit Provider,同时传递AuditEvent对象，当AuditEvent和Auditing Provider配置的需要Audit的信息相同时，将被记录到文件系统，数据库或其他存储介质中。<br /><br />·Credential Mapping Process <br /><br /><span lang="EN-US"><img height="247" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image014.gif" width="551" v:shapes="_x0000_i1036" /></span><br /><br />最初由应用组件（包括jsp,ejb,resource adapter）访问EIS系统（包括Oracle,SQL Server或其他）调用 WebLogic security framework时引发。应用组件传递subject(who),weblogic resource(what)和需要的credential。WebLogic security framework将请求传递给credential mapping provider,credential mapping provider处理请求，并将数据库中的credential回送weblogic security framework,最后将credential返回给组件层，组件层使用credential以访问EIS系统。<br /><br />·weblogic security provider</p><p>缺省自带的security provider,如果不能满足你的需求，可以自己开发客户化的security provider.<br />→ 从weblogic.security.spi包中实现正确的SSPIs(security service provider interface)以生成security provider.<br />→ 建立Mbean的定义文件（MDF）,使用Weblogic MbeanMaker工具生成Mbean类型。<br /><br /><span lang="EN-US"><img height="377" alt="" src="http://dev2dev.bea.com.cn/images/article/server030602/image015.gif" width="553" v:shapes="_x0000_i1037" /></span></p><p>weblogic authentication provider</p><p>代替了6.xFile realm的功能，支持用户名/密码认证，利用embedded LDAP Server存储用户和组的信息。</p><p>支持LDAP Authentication访问外部的LDAP 存储（Open LDAP,Netscape iPlanet,Microsoft Active Directory,Novell NDS）</p><p>WebLogic Identity Assertion Provider</p><p>支持X.509认证和CSIv2(CORBA Common Secure Interoperability version 2)身份认证。<br />WebLogic Identity Assertion provider验证token 的类型，然后将X.509数字认证和x.501的单一的名字和WebLogic 的用户名相对应。同样指定一信任的客户principal列表便于CSIv2的身份确认。统配符*用于指定所有的信任的principal.如果客户没有列入到信任的principal中，CSIv2 Identity Assertion将失败。<br />支持的token类型有<br /></p>→ AU_TYPE -- WebLogic authenticationdUser<br />→ X509_TYPE X.509客户端<br />→ CSI_PRINCIPAL_TYPE CSIV2 principal name<br />→ CSI_ANONYMOUS_TYPE CSIV2 anonymous identity<br />CSI_X509_CERTCHAIN_TYPE CSIv2 X.509 certificate chain identity<br />→ CSI_DISTINGUISHED_NAME_TYPE CSIv2 distinguished name identity<br /><p>WebLogic principal validation provider<br />WebLogic authorization provider<br />WebLogic adjudication provider<br />WebLogic role mapping provider<br />·WebLogic auditing provider<br />WebLogic credential mapping provider<br />以上provider功能在前面已经做了基本介绍。在此省略。<br /><br />·Weblogic keystore provider<br />使用sun公司sdk提供的keystore实现，利用标准的JKS keystore类型，将keystore做为文件（每台机一个）来保存。有两种keystore 文件<br />→ 一个文件保存CA认证(CA certificates)<br />→ 另一个文件保存server的private key<br /><br />·WebLogic realm adapter provider<br />主要用来提供和6.x安全域的兼容。Realm Adapter提供<br />Authentication(include Identity Assertion Provider)<br />Authorization<br />Auditing<br />Adjudication</p><p>仅仅用于升级且不建议使用。<br /><!--文章其他信息--></p><div class="dot001"><img height="1" alt="" src="http://dev2dev.bea.com.cn/images/_.gif" width="100%" /></div><table cellspacing="0" cellpadding="3" width="100%" border="0"><tbody><tr valign="bottom"><td colspan="2" height="20"> <span class="h2b">作者简介</span></td></tr><tr><td valign="top" align="middle"></td><td>聂健是（dev2dev ID: ericnie） Horizon Software Ltd. 技术顾问</td></tr></tbody></table><img src ="http://www.blogjava.net/beauty_beast/aggbug/74500.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/beauty_beast/" target="_blank">柳随风</a> 2006-10-11 10:50 <a href="http://www.blogjava.net/beauty_beast/archive/2006/10/11/74500.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转载）自定义WebLogic LDAP Authentication Provider</title><link>http://www.blogjava.net/beauty_beast/archive/2006/10/11/74495.html</link><dc:creator>柳随风</dc:creator><author>柳随风</author><pubDate>Wed, 11 Oct 2006 02:45:00 GMT</pubDate><guid>http://www.blogjava.net/beauty_beast/archive/2006/10/11/74495.html</guid><wfw:comment>http://www.blogjava.net/beauty_beast/comments/74495.html</wfw:comment><comments>http://www.blogjava.net/beauty_beast/archive/2006/10/11/74495.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/beauty_beast/comments/commentRss/74495.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/beauty_beast/services/trackbacks/74495.html</trackback:ping><description><![CDATA[
		<p align="center">
				<span class="h1b">
						<strong>自定义WebLogic LDAP Authentication Provider</strong>
						<br />
				</span>
				<br />
				<br />
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td height="64">时间：2005-04-20<br />作者：<a href="http://dev2dev.bea.com.cn/author/55.html">马晓强</a><br />浏览次数：
<script language="JavaScript" src="http://203.81.25.103/cgi-bin/beadevcount.cgi?d_id=502" type="text/JavaScript"></script>
 152611 <br />本文关键字：</td>
								<td>
										<table class="box_content" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<!-- 提取技术文章 -->
		<div class="beas">
				<img height="1" alt="" src="http://dev2dev.bea.com.cn/images/dot6B6B6B.gif" width="100%" />
		</div>目录 
<p><a href="http://dev2dev.bea.com.cn/techdoc/200504502.html#1">1 J2EE Security 和 LDAP Security</a><br /><a href="http://dev2dev.bea.com.cn/techdoc/200504502.html#2">2 JAAS和WebLogic Security Framework</a><br /><a href="http://dev2dev.bea.com.cn/techdoc/200504502.html#3">3 了解WebLogic LDAP Authentication Provider</a><br /><a href="http://dev2dev.bea.com.cn/techdoc/200504502.html#4">4 定制自己的Custom LDAP Authentication Provider</a><br /><a href="http://dev2dev.bea.com.cn/techdoc/200504502.html#5">5 部署中的注意事项</a><br /><a href="http://dev2dev.bea.com.cn/techdoc/200504502.html#6">6 结束语</a><br /><a href="http://dev2dev.bea.com.cn/techdoc/200504502.html#7">7 参考资料</a></p><p>　　从WebLogic Server 7.0开始，WebLogic Server的安全机制有了全面的改变，实现了一个更加规范的基于JAAS的Security Framework，以及提供了一系列设计良好的Security Service Provider Interface。这样我们可以根据自己的具体需求，通过Custom Security Authentication Provider来实现安全上的定制功能。</p><p>　　本文将以WebLogic（WebLogic Server 8.1） Security和 LDAP为基础，介绍Custom LDAP Authentication Provider如何给我们带来更多的灵活性，和系统安全设计上更多的空间；以及讨论如何实现一个Custom LDAP Authentication Provider和部署过程中的一些良好经验。</p><p>　　由于本文涉及到的范围太广，不可能一一详细讨论；为了使没有相关基础的读者也能够阅读理解本文，因此我将在文章前半部分，试图通过最简洁扼要的描述，来使大家对于J2EE Security，WebLogic Security Framework以及LDAP 等有一个初步的清晰认识；进而可以开发出自己的LDAP Authentication Provider。因此很多地方做了比较有限的描述或者介绍，更多详细的内容可以参考文后附带的参考资料或者文中给出的链接。<a id="1" name="1"></a><br /><br /><b>1 J2EE Security 和 LDAP Security</b><br />　　Sun J2EE推出以来，其安全部分的规范就一直倍受关注。我们最常见到安全规范的两个方面分别是Servlet Security 和 EJB Security。目前绝大多数的Servlet容器，J2EE容器都能很好的支持这些安全规范。</p><p>　　WebLogic Server作为业界领先的J2EE服务器对J2EE Security的支持是非常优秀的。我们这里将结合WebLogic Security和使用越来越广泛的LDAP做一个简要的介绍，这些是设计开发Custom LDAP Authentication Provider的技术基础。</p><p><b>1.1 Authentication 和Authorization<br /></b>　　这里需要大家先明确安全上的两个重要名词：一个是认证（Authentication），一个是授权（Authorization）。认证是回答这个人是谁的问题，即完成用户名和密码的匹配校验；授权是回答这个人能做什么的问题。我们讨论的J2EE Security包括Declarative Authorization和Programmatic Authorization，即一个是通过web.xml，ejb-jar.xml等部署描述符中的安全声明通过容器提供的服务来完成权限控制的；一个是通过HttpServletRequest.isUserInRole()和EJBContext.isCallerInRole()这样的编程接口在应用中自己完成权限控制的。</p><p><b>1.2 资源（Resource）和Security Role<br />　　</b>资源原本只包括 Web Resource和EJB Resource，但在WebLogic Security中扩展到几乎任何一个WebLogic Platform中的资源，具体可以参考<a href="http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777" target="_blank">http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777</a>。授权就是针对资源的访问控制。</p><p>　　J2EE Security是基于Security Role的。我们可以将一组资源与一个Security Role进行关联来达到控制的目的——只有拥有该Role权限的用户才能够访问这些资源。简单的说，我们可以通过给用户分配不同的Security Role来完成权限的控制。复杂的情况下包括用户/用户组，以及Principal和Role的映射关系等等。下面是一个声明性安全在web application（war包中WEB-INF/web.xml）中的示例：</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>&lt;web-app&gt;<br />　&lt;security-constraint&gt;<br />　&lt;web-resource-collection&gt;<br />　　&lt;web-resource-name&gt;Success&lt;/web-resource-name&gt; <br />　　&lt;url-pattern&gt;/welcome.jsp&lt;/url-pattern&gt; <br />　　　&lt;http-method&gt;GET&lt;/http-method&gt; <br />　　　&lt;http-method&gt;POST&lt;/http-method&gt; <br />　&lt;/web-resource-collection&gt; <br />　&lt;auth-constraint&gt; <br />　　&lt;role-name&gt;webuser&lt;/role-name&gt; <br />　&lt;/auth-constraint&gt; <br />　&lt;/security-constraint&gt; <br />　&lt;login-config&gt; <br />　　&lt;auth-method&gt;BASIC&lt;/auth-method&gt; <br />　　&lt;realm-name&gt;default&lt;/realm-name&gt; <br />　&lt;/login-config&gt; <br />　&lt;security-role&gt; <br />　　&lt;role-name&gt;webuser&lt;/role-name&gt; <br />　&lt;/security-role&gt;<br />&lt;/web-app&gt;</td></tr></tbody></table><p>　　只有拥有角色webuser的用户才能够访问welcome.jsp页面，否则容器会返回401无权访问的错误。更多信息请参考<a href="http://e-docs.bea.com/wls/docs81/security/index.html" target="_blank">http://e-docs.bea.com/wls/docs81/security/index.html</a>。</p><p>　　同时我们需要在weblogic.xml（war包中WEB-INF/weblogic.xml）中对security role和principal进行映射关系的配置：</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>&lt;weblogic-web-app&gt; <br />　&lt;security-role-assignment&gt; <br />　　&lt;role-name&gt;PayrollAdmin&lt;/role-name&gt; <br />　　&lt;principal-name&gt;Tanya&lt;/principal-name&gt; <br />　&lt;/security-role-assignment&gt;<br />&lt;/weblogic-web-app&gt;</td></tr></tbody></table><p>　　这样拥有Principal “Tanya”的用户（Principal将封装到Subject中，用户将和Subject关联）将会拥有PayrollAdmin的权限。</p><p>　　<i>注意：一般情况下为了简化设计，本文中将假设security role即是principal name（如果不配置security-role-assignment，WebLogic会默认做此假设）。即上例中Principal-name也为PayrollAdmin。</i></p><p><b>1.3 LDAP Security<br />　　</b>LDAP是轻量级目录服务（Lightweight Directory Access Protocol）。越来越多的应用开始采用LDAP作为后端用户存储。在安全上，LDAP Security是基于ACL（Access Control List）的，它通过给一个用户组分配LDAP 操作资源（比如对一个子树的查询，修改等）来最终完成权限的控制。因此在LDAP中，授权工作是以用户组为单位进行的。一个用户组一般来说是拥有如下一组属性的LDAP Entry：</p><p align="center"><img height="165" src="http://dev2dev.bea.com.cn/images/webser/image2005041901.gif" width="500" /><br />图1-3-1</p><p>　　其中objectclass可以为groupOfUniqueNames或者groupOfNames，它们对应的组成员属性分别是uniquemember和member。如果是动态组，objectclass为groupOfURLs。动态组一般应用在成员可以通过某种业务逻辑运算来决定的情况下。比如，经理为ZHANGSAN的全部员工。下面是一个典型的动态组，memberURL属性定义了哪些entry属于该组：</p><p align="center"><img height="130" src="http://dev2dev.bea.com.cn/images/webser/image2005041902.gif" width="550" /><br />图1-3-2</p><p><b>　　</b>从图1-3-1中我们可以看出，用户WANTXIAOMING，ZHANGSAN，LISI属于组HR Managers。这种组和成员的关系是通过属性uniquemember来决定的。同时LADP Group 支持嵌套，即一个组可以是另外一个组的成员，比如我们将Accounting Managers组分配给HR Managers组作为其成员：</p><p align="center"><img height="185" src="http://dev2dev.bea.com.cn/images/webser/image2005041903.gif" width="500" /><br />图1-3-3</p><p><b>　　</b>这样将表示Accounting Managers中的成员，同时也是组HR Managers的成员。通过这种层级关系可以使权限分配变的更加灵活。</p><p><b>　　</b>下面是一些名词的解释，希望大家对LDAP有更好的理解：<br /><b>　　</b>a) Objectclass —— LDAP对象类，抽象上的概念类似与一般我们理解的class。根据不同的objectclass，我们可以判断这个entry是否属于某一个类型。比如我们需要找出LDAP中的全部用户：(objectclass=person)再比如我们需要查询全部的LDAP组：(objectclass=groupOfUniqueNames)<br /><br /><b>　　</b>b) Entry —— entry可以被称为条目，或者节点，是LDAP中一个基本的存储单元；可以被看作是一个DN和一组属性的集合。 属性可以定义为多值或者单值。<br /><br /><b>　　</b>c) DN —— Distinguished Name，LDAP中entry的唯一辨别名，一般有如下的形式：uid=ZHANGSAN, ou=staff, ou=people, o=examples。LDAP中的entry只有DN是由LDAP Server来保证唯一的。</p><p><b>　　</b>d) LDAP Search filter ——使用filter对LDAP进行搜索。 Filter一般由 (attribute=value) 这样的单元组成，比如：(&amp;(uid=ZHANGSAN)(objectclass=person)) 表示搜索用户中，uid为ZHANGSAN的LDAP Entry．再比如：(&amp;(|(uid= ZHANGSAN)(uid=LISI))(objectclass=person))，表示搜索uid为ZHANGSAN, 或者LISI的用户；也可以使用*来表示任意一个值， 比如(uid=ZHANG*SAN)，搜索uid值以 ZHANG开头SAN结尾的Entry。更进一步，根据不同的LDAP属性匹配规则，可以有如下的Filter： (&amp;（createtimestamp&gt;=20050301000000）(createtimestamp&lt;=20050302000000))，表示搜索创建时间在20050301000000和20050302000000之间的entry。<br /><b>　　</b>Filter中 “&amp;” 表示“与”；“!”表示“非”；“|”表示“或”。根据不同的匹配规则，我们可以使用“=”，“~=”，“&gt;=”以及“&lt;=”，更多关于LDAP Filter读者可以参考LDAP相关协议：<a href="http://www.ietf.org/rfc/rfc2254.txt" target="_blank">http://www.ietf.org/rfc/rfc2254.txt</a>。</p><p><b>　　</b>e) Base DN —— 执行LDAP Search时一般要指定basedn，由于LDAP是树状数据结构，指定basedn后，搜索将从BaseDN开始，我们可以指定Search Scope为：只搜索basedn（base），basedn直接下级（one level），和basedn全部下级（sub tree level）。</p><p><b>　　</b>下面是一个典型的LDAP Tree结构，右侧显示Entry uid=ZHANGSAN, ou=staff, ou=people, o=examples的属性，该entry代表了一个名字叫张三的用户：</p><p align="center"><img height="328" src="http://dev2dev.bea.com.cn/images/webser/image2005041904.gif" width="600" /><br />图1-3-4<a id="2" name="2"></a></p><p><b>2 JAAS和WebLogic Security Framework</b><br /><b>　　</b>现在越来越多的人开始了解JAAS，使用JAAS。WebLogic Security Framework就是基于JAAS的。因此我们需要对此有一个非常准确的理解才能够设计开发Custom Authentication Provider。</p><p><b>　　</b>下面我们从几个名词入手，了解JAAS和 WebLogic Security Framework的关键之处：</p><p><b>2.1 Principal，Subject和LoginModule<br />　　</b>a) Principal <br /><b>　　</b>当用户成功验证后，系统将会生成与该用户关联的各种Principal。我们这里将Principal进行简化的设计，认为一个Principal就是用户登录帐号和它所属于的组（LDAP Group）。这样当用户登录成功后，我们将会在LDAP中执行搜索，找出用户属于哪些组，并将这些组的名字，或者其标识作为Principal返回。这样，当用户在LDAP中属于某一个组，并且这个组的名字对应到 web.xml （或者ejb-jar.xml）中的Security role，那么这个用户就可以看作拥有访问这个Security Role定义的资源的权限。<br /><b>　　</b>在WebLogic Security Framework中，这个LDAP Group的名字（Principal）和Security Role的映射关系，可以通过一个 Role Mapping Provider来实现动态的匹配，即用户的动态权限控制。比如在运行时根据某一个业务逻辑来决定用户是否拥有某一个权限。关于Role Mapping Provider，读者可以参考下面链接的内容：<a href="http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542" target="_blank">http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542</a>。</p><p><b>　　</b>b) Subject<br /><b>　　</b>JAAS规定由Subject封装用户以及用户认证信息，其中包括Principals。下面是WebLogic Security Framework中Subject的组成图示：</p><p align="center"><img height="385" src="http://dev2dev.bea.com.cn/images/webser/image2005041905.gif" width="500" /><br />图2-1-1</p><p><b>　　</b>这样当用户试图访问一个受限的J2EE资源时，比如一个web URL，或者一个 EJB Method（可以在web.xml或者ejb-jar.xml中定义，由Security Role控制），WebLogic Security Framework将会通过 Authorization Provider检查用户当前的Subject中是否包含有是否可以访问受限资源的Principals。由于Principals将和J2EE Security Role在weblogic.xml中定义一个映射关系（或者通过其他业务逻辑来确定这种关系），因此通过这样的关系，可以最终知道用户是否有某一个J2EE Resource的访问权限。</p><p><b>　　</b>c) LoginModule<br /><b>　　</b>JAAS LoginModule是一个Authentication Provider必须的组成部分。LoginModule是认证的核心引擎，它负责对用户身份进行验证，同时将返回与用户关联的Principals（用户登录帐号，以及LDAP Groups），然后放入Subject中，供后续的访问控制使用。<br /><b>　　</b>我们将在LoginModules中完成LDAP的相关认证，查询操作，将用户在LDAP中所属于的组搜索出来，作为认证后的结果封装到Subject中返回。</p><p><b>2.2 WebLogic Authentication认证过程</b><br /><b>　　</b>下面我们了解一下WebLogic的认证过程。以下图片来自<a href="http://e-docs.bea.com/wls/docs81/dvspisec/atn.html" target="_blank">http://e-docs.bea.com/wls/docs81/dvspisec/atn.html</a> 我将其中主要部分进行说明。</p><p align="center"><img height="452" src="http://dev2dev.bea.com.cn/images/webser/image2005041906.gif" width="600" /><br />图2-2-1</p><p><b>　　</b>Security Framework在WebLogic Server启动时初始化Authentication Provider（5）。当有认证请求进入时，Security Framework首先将通过AuthenticationProvider.getLoginModuleConfiguration()来获取一个AppConfigurationEntry对象。通过AppConfigurationEntry（详见<a href="http://java.sun.com/security/jaas/apidoc/javax/security/auth/login/AppConfigurationEntry.html" target="_blank">http://java.sun.com/security/jaas/apidoc/javax/security/auth/login/AppConfigurationEntry.html</a> ）可以初始化一个LoginModule。初始化LoginModule的方法为：public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)，可以看到里面有Subject, CallbackHandler等等重要参数。<br /><b>　　</b>被实例化的JAAS LoginModule将完成用户的一系列验证任务。验证完成后，在（6a）中principals将被Principal Validation Provider签名，在（ 6b）中存放到与用户关联的Subject里面。Security Framework最后将拿着该用户的Subject去完成其他权限控制等任务。<a id="3" name="3"></a><br /><br /><b>3 了解WebLogic LDAP Authentication Provider</b><br /><b>　　</b>现在我们有信心了解一下WebLogic LDAP Authentication Provider的工作原理了。这里将以WebLogic提供的iPlanet Authentication Provider的配置为例进行说明。在这里也需要明确说明一下，为了方便进行描述，我们将实际属于LoginModule的行为也一并归结到Provider中。没有单独将两个的行为分开，目的是为了突出整个完整的过程。</p><p><b>3.1 iPlanet Authentication Provider配置</b></p><p align="center"><img height="454" src="http://dev2dev.bea.com.cn/images/webser/image2005041907.gif" width="600" /><br />图3-1-1</p><p><b>　　</b>从上图可以看出我们需要指定LDAP服务器的地址，端口，连接LDAP使用的Principal（不同于前面讨论的Principal，这个Principal实际是一个连接LDAP的用户，也就是一个LDAP 中用户Entry的 DN，它必须要有相关的LDAP 搜索等权限）和Credential（一般来说就是口令）。<br /><b>　　</b>再看下面关于Users的配置：</p><p align="center"><img height="454" src="http://dev2dev.bea.com.cn/images/webser/image2005041908.gif" width="600" /><br />图3-1-2</p><p><b>3.1.1 User Object Class </b>—— 前面已经对objectclass进行过说明，指明LDAP Entry属于哪一类<br /><b>3.1.2 User Name Attribute </b>—— 用户登录帐号在LDAP Entry中的属性，一般为UID或者cn<br /><b>3.1.3 User Base DN</b> —— 所有的用户将会放置到这个子树下面，因此在Provider中对用户进行的搜索将会从这个basedn开始<br /><b>3.1.4 User Search Scope </b>—— 指定搜索范围为Basedn的直接一级或者全部下级<br /><b>3.1.5 User From Name Filter</b> —— 使用这个filter可以搜索出用户信息。其中%u将会被用户输入的登录帐号替换，从而查询中LDAP中的用户信息</p><p align="center"><img height="452" src="http://dev2dev.bea.com.cn/images/webser/image2005041909.gif" width="600" /><br />图3-1-3</p><p><b>3.1.6 Group Base DN </b>—— 从该Base DN开始搜索用户组<br /><b>3.1.7 Group From Name Filter</b> —— %g将会被组的名字替换，通过该filter可以搜索出符合条件的LDAP Group<br /><b>3.1.8 Static group name attribute</b> —— 组名字的属性，属性cn对应的值就是组的名字</p><p align="center"><img height="469" src="http://dev2dev.bea.com.cn/images/webser/image2005041910.gif" width="600" /><br />图3-1-4</p><p><b>3.1.9 Static Member DN Attribute</b> —— 静态成员属性，通过该属性可以判断一个Entry是否属于一个组<br /><b>3.1.10 Static Group DNs From Member DN Filter</b> —— 通过该filter可以找出用户属于哪些组<br /><b>3.1.11 Dynamic Group</b> —— 动态组是在运行时根据某种业务逻辑，来决定成员隶属关系的LDAP Group</p><p><b>3.2 iPlanet Authentication Provider的工作原理</b><br /><b>　　</b>从上面配置的介绍中可以看出，后端存储为LDAP的情况下，在Console中我们需要配置的参数已经清楚的表明它所要完成工作的内容。<br /><b>　　</b>首先，它使用配置的User BaseDN和Filter，来根据用户输入的登录帐号进行搜索，找出存放在LDAP中的用户Entry。如果找到一个用户，那么Provider就使用该用户的DN和用户输入的口令，进行验证。验证可以使用LDAP Bind和LDAP Compare，这需要根据不同LDAP的特点来进行选择。<br /><br /><b><i>　　</i></b><i>A. LDAP Bind —— 将当前的LDAP Connection绑定到一个用户身份上。这样后续的使用该Connection的LDAP Operation都将以该身份进行。LDAP Bind需要两个重要参数，一个是用户Entry的DN，一个是该用户的口令。</i></p><p><i><b>　　</b>B. LDAP Compare —— LDAP Compare是一个为兼容X.500的古老操作，它用于检查一个属性值是否包含在指定Entry中的属性里。这样我们可以在知道用户password存放在哪个属性的前提下，对该属性进行compare。如果成功，表明口令正确。如果属性值为散列后的口令（绝大多数情况），有的LDAP Server支持这样的验证，有的不支持，比如iPlanet LDAP Server 5。</i></p><p><b>　　</b>验证成功后，Provider将使用Console中关于Groups和Memberships中的配置，查找用户属于哪些LDAP Group，而且由于这些组本身可能会有一些嵌套，因此对于搜索到的组还需要进行查询。即使用filter： (&amp;（uniquemember=uid=ZHANGSAN,ou=staff,ou=people,o=examples）(objectclass=groupOfUniqueNames))从Group Base DN开始搜索，将返回用户所属的第一层次Group；然后对于这些返回的组DN，仍然需要使用上面的Filter进行搜索（uniquemember值替换为组的DN），找出嵌套关系，直到查询完成没有组嵌套为止（此处需要防止陷入嵌套的循环中，比如Group A 包含了Group B； Group B又包含了Group A，有的LDAP Server可以自动检测出，有的需要我们程序来判断）。<br /><br /><b>　　</b>然后将用户登录的帐号，用户所属组的名字（属性CN的值或其他），放入Subject中。最后调用Principal Validator Provider，对Subject中的principals进行签名，来表明该Subject是这个Provider生成的。这样防止其他攻击者伪造Subject以及Principal进行欺骗。此处也可以解释为何在不同的WLS Domain间不能够传递Subject，我们可以通过设置域信任来完成这种Subject的传递。设置域信任使用的Credential就是签名用的Key。<br /><br /><b>　　</b>图3-1-5表明了如何设置WebLogic Domain Credential，默认情况下WebLogic Server会在启动的时候随即生成一个Credentials（在WLS6.1时，这个值就是system用户的口令）：</p><p align="center"><img height="307" src="http://dev2dev.bea.com.cn/images/webser/image2005041911.gif" width="600" /></p><p><b>　　</b>可以想见如果我们实现自己的Principal Validator Provider，让它去一个集中的验证服务器中对Subject进行签名，或者验证Subject，这样就可以实现域信任，进而完成Application（EJB Tier）层的SSO。</p><p><b>　　</b>通过以上的讨论，我们对于实现自己的LDAP Authentication Provider是不是又增加了一份信心？<a id="4" name="4"></a><br /><b><br />4 定制自己的Custom LDAP Authentication Provider</b><br /><b>　　</b>为何要定制自己的Authentication Provider? 由于WebLogic Server已经提供了很多默认的Authentication Provider在一般情况下我们确实没有必要实现自己的Provider。但是面对某些针对安全方面的复杂需求时，WebLogic Server提供的Provider很有可能不满足这些需求，此时就需要我们定制自己的Provider。</p><p><b>　　</b>在这一章的开头部分中我需要简要讨论关于WebLogic MBean Types，以及WebLogic Console扩展等内容，目的在于让读者了解到我们通过WebLogic Console可以完成对Custom Security Provider的配置和部署，我将以WebLogic 提供的Sample Security Provider为示例进行说明。详细的信息可以参考以下的一些资源：</p><p><a href="http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106272" target="_blank">http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106272</a><br /><a href="http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106241" target="_blank">http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106241</a></p><p><b>　　</b>上面两个链接描述了如何创建MBean Types以及在控制台上配置Custom Authentication Provider.下面这个链接中专门介绍了WebLogic Console的扩展：</p><p><a href="http://dev2dev.bea.com.cn/techdoc/2005012102.html" target="_blank">/techdoc/2005012102.html</a></p><p><b>　　</b>读者可以从<a href="http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp" target="_blank">http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp</a>下载WLS提供的Sample Security Provider。</p><p><b>4.1 MBean Types和WebLogic Console</b><br /><b>　　</b>一般情况下，人们可能更习惯通过WebLogic Console对Security Provider进行配置。这里我将简要描述这个过程，以及可以到达的一个效果。限于篇幅就不详细讨论了。</p><p><b>　　</b>从weblogic.management.security.authentication.Authenticator扩展MBean Types。 MBean Types是MBean（http://java.sun.com/products/JavaManagement/wp/）的工厂，我们扩展SampleSecurityProviders81 包中的SimpleSampleAuthenticator.xml（MBean Definition File），增加一个我们自定义的参数LDAP Server IP：</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>&lt;MBeanAttribute<br /><b>　　</b>Name = "LDAPServerIP" <br /><b>　　</b>Type = "java.lang.String" <br /><b>　　</b>Writeable = "true" <br /><b>　　</b>Default = "&amp;quot;127.0.0.1&amp;quot;" <br />/&gt;</td></tr></tbody></table><p><b>　　</b>这样在Provider中我们将通过MbeanMaker(WLS提供)生成的SimpleSampleAuthenticatorMBean中取到这个属性：SimpleSampleAuthenticatorMBean.getLDAPServerIP()。MBean将在初始化Provider的时候作为参数传入。这样我们就可以通过MBean中的参数控制Provider的行为。</p><p><b>　　</b>当然这个参数是可以在WebLogic Console中设置的，通过对MBean Types的扩展，在WebLogic Console上看到的画面如下：</p><p align="center"><img height="210" src="http://dev2dev.bea.com.cn/images/webser/image2005041912.gif" width="600" /><br />图4-1-1</p><p><b>　　</b>这样我们可以通过Console修改配置参数（修改的Security Provider参数将保存在config.xml中，默认的值将保存在MBean Jar File中）。</p><p><b>4.2 为何定制LDAP Authentication Provider<br />　　</b>当我们面临越来越复杂的安全方面的业务需求时，或者面临较高的性能要求，需要根据目标LDAP做针对性的优化时，或者需要将我们已有的认证，或授权模块集成到WebLogic平台时，WebLogic提供的现成的Provider往往不能满足我们的需求。</p><p><b>4.2.1 复杂的业务需求<br /></b><b>　　</b>当系统要求用户不仅仅输入用户名（j_username），口令（j_password），还需要输入其他信息，比如登录的地点，系统的名字，用户的类型等等。如果是采用基于J2EE Form的验证方式， 登录信息需要提交到j_security_check（Servlet规范定义由容器负责实现的Servlet），导致我们没法处理更多的信息。</p><p><b>　　</b>这个时候，如果能够实现我们自己的 Authentication Provider，那么我们就可以通过TextInputCallback来获取登录表单中更多的信息了；进而通过这些信息在Provider中完成符合我们需要的处理。</p><p><b>　　</b>比如搜狐的登录页面上需要选择用户的类型：</p><p align="center"><img height="176" src="http://dev2dev.bea.com.cn/images/webser/image2005041913.gif" width="158" /><br />图4-2-1</p><p><b>4.2.2 性能需求或者调优</b><br />　　有时，有的用户会比较困惑为何WebLogic LDAP Security Provider在压力测试中的表现不很理想，用户需要较长时间的等待，才能够登录到系统中。由于这些Provider是 WebLogic产品的一部分，因此缺乏对不同目标LDAP Server的有针对性的优化。这样就使得我们无法充分发挥具体LDAP Server的性能调优。</p><p>　　比如，有的LDAP Server支持动态组（LDAP Dynamic Group，成员关系是运行时根据ldap server，basedn，filter等动态决定的），可以使用如下的Filter查询用户属于哪些动态组：</p><p>　　(uniquemember=uid=MAXQ,ou=staff,ou=people,o=examples)</p><p>　　有的LDAP Server虽然支持动态组，但是支持的有限，不能使用上面的Filter获取用户属于哪些动态组。在WebLogic iPlanet Authentication Provider的实现中，它先是搜索出全部的动态组，然后再遍历这些动态组，依次去LDAP中检查用户是否属于一个组；很明显，这样虽然最大程度的满足了不同LDAP Server的要求（从产品的角度讲可能是必须的），但是与LDAP交互的次数大增，并发用户量一大性能下降的比较明显。</p><p>　　此时，如果系统中的LDAP支持上面的Filter或者有更好的搜索方式，那么完全可以通过定制Provider完成对性能的优化。</p><p><b>4.2.3 已有权限控制的集成</b><br />　　如果系统中已经存在了现成的满足需求的认证模块，并且已经很好的工作；在系统转向J2EE架构，并使用WebLogic Server做J2EE容器时，我们可能会更愿意直接在Provider中加入这个认证模块。</p><p>　　综上，我只是列举了一些可以驱动我们开发自己Provider的需求，相信在读者实际工作中可能会面临更复杂的情况，开发自己的Provider将是一个非常好的选择。</p><p><b>4.3 LDAP Authentication Provider实现</b><br />　　本文之前为了表述的方便没有单独提到LoginModule，认为LoginModule的行为就是LDAP Authentication Provider的行为。到了目前的具体实现阶段，我们必须分开Authentication Provider和JAAS LoginModule。最终部署到WebLogic上的实际只是LDAP AuthenticationProvider Implements。</p><p>　　WebLogic SecurityFramework通过Authentication Provider获取具体的JAAS LoginModule。通过LoginModule完成最终登录的工作。因此我们必须先实现一个AuthenticationProvider。</p><p>　　我们一般通过weblogic.security.spi.AuthenticationProvider 来实现自己的AuthenticationProvider。这里介绍其中的几个重要方法：</p><p>　　a) public void initialize(ProviderMBean mbean, SecurityServices services)<br />初始化一个Provider。通过参数MBean我们可以获取到在WebLogic Console中配置的各项参数。进而初始化我们的Provider，然后通过Provider传递到LoginModule中。</p><p>　　b) public void shutdown()<br />释放一些与Provider，LoginModule等相关的资源。</p><p>　　c) public AppConfigurationEntry getLoginModuleConfiguration()<br />这个方法非常重要，通过该方法，WebLogic Security Framework可以获取用于初始化LoginModule的AppConfigurationEntry。AppConfigurationEntry中存放了LoginModule的类名等信息，比如使用如下代码返回一个AppConfigurationEntry:</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>　　return new AppConfigurationEntry(<br />　　　　"examples.security.providers.authentication.SampleLoginModuleImpl",<br />　　　　controlFlag,<br />　　options);</td></tr></tbody></table><p>　　其中LoginModule Name就</p><p>是"examples.security.providers.authentication.SampleLoginModuleImpl"，我们通过它就可以实例化一个LoginModule并通过LoginModule.initialize()方法进行初始化。</p><p>　　d) public AppConfigurationEntry getAssertionModuleConfiguration()<br />该方法将返回一个与Identity Assertion Provider关联的LoginModule。这个Assertion LoginModule，将只会验证用户是否存在，以及如果存在返回用户的Principals。 该方法也比较重要，需要正确实现，比如我们使用CLIENT-CERT这种WEB认证方式，该方法就会被调用。</p><p>　　Provider的实现比较简单，读者可以在<a href="http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp" target="_blank">http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp</a>下载WebLogic提供的Samples，查看SampleAuthenticationProviderImpl的代码。</p><p></p><p><b>4.4 LDAP LoginModule 逻辑流程<br /></b>　　实现了Provider后，必须拥有我们自己的LDAP LoginModule。下面是一个简单的用于演示的验证逻辑流程图。实际的一个LoginModule由于不同的业务需求，情况可能会复杂得多。这里只是描述了最核心最基本的逻辑，使读者能有一个清晰的思路。后面我将以这个流程为例进行实现。</p><p align="center"><img height="743" src="http://dev2dev.bea.com.cn/images/webser/image2005041914.gif" width="356" /></p><p><b>4.5 LoginModule代码示例和讲解<br /></b>　　这里我将使用Netscape LDAP SDK for java作为开发工具实现LDAP相关的操作，读者可以到<a href="http://docs.sun.com/db/doc/816-6402-10" target="_blank">http://docs.sun.com/db/doc/816-6402-10</a> 下载开发手册，从<a href="http://www.mozilla.org/directory/" target="_blank">http://www.mozilla.org/directory/</a> 下载SDK 包。一般来说还可以通过JNDI来操作LDAP，我个人认为Sun LDAP JNDI Provider中关于Connection Pool的实现非常优秀。但不管使用哪种SDK，对LDAP的编程原理上基本都是相同的（因为基于同样的LDAP协议），不同的可能仅仅是接口，类的名字而已。</p><p><b>4.5.1 初始化Connection Pool</b><br />　　为了有效使用宝贵，并且有限的LDAP连接，必须使用连接池。下面的代码初始化了一个LDAP连接池：</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>/** <br />* <br />*/ <br />static ConnectionPool pool; <br />/** * * LDAPException <br />*/ public LdapClient() <br />　　throws LDAPException <br />　　　　{ <br />　　　　　　pool= new ConnectionPool( <br />　　　　　　　　1, 150, "127.0.0.1", 389, "cn=Directory Manager", "88888888"); <br />}</td></tr></tbody></table><p>　　Sun JNDI LDAP Service Provider（JDK1.4）中可以通过在环境变量中设置具体的参数来启用连接池，达到复用连接的目的。具体可以参考链接：<a href="http://java.sun.com/products/jndi/tutorial/ldap/connect/index.html" target="_blank">http://java.sun.com/products/jndi/tutorial/ldap/connect/index.html</a>，下面是示例代码：<br /><br />Hashtable env= new Hashtable();<br />…<br />env.put("com.sun.jndi.ldap.connect.pool", "true");<br />DirContext ctx= new InitialDirContext( env);</p><p><b>4.5.2 根据用户输入的登录帐号，搜索用户Entry<br /></b>　　下面这个方法实现了从LDAP中搜索用户Entry DN的最简单的过程。实际上我们可以在其中实现很多定制的功能。比如允许用户使用多于一个的帐号登录，只要这些帐号能够通过LDAP Searche最终返回一个唯一的用户DN即可。</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>  * <br />   * LDAPException <br />   */<br />  public String getUserDN( String uid) throws LDAPException{ <br />    LDAPConnection conn= pool.getConnection();<br />    try {<br />      String[] attrs= new String[]<br />{}; <br />      LDAPSearchResults sr= conn.search("o=examples", 2, "(uid="+ uid +")", attrs, false);<br />      if ( sr.hasMoreElements()) { <br />        LDAPEntry entry= sr.next();<br />        return entry.getDN();<br />}<br />    throw new LDAPException("No Such Object:"+ uid, <br />        LDAPException.NO_SUCH_OBJECT);<br />    }catch ( LDAPException ex) { <br />      throw ex;<br />    }finally {<br />      try {<br />        if (conn!= null) pool.close(conn);<br />      }catch ( Exception ex)<br />{}<br />    }<br />  }</td></tr></tbody></table><p>　　首先需要从池中获取一个LDAP连接，然后使用LDAPConnection.search方法进行搜索。我这里以一个典型的LDAP Search接口为例进行说明，其他API比如JNDI等，基于同样的LDAP协议，接口中同样参数的含义都是相同的。</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>public LDAPSearchResults search(java.lang.String base,<br />　　int scope,<br />　　java.lang.String filter,<br />　　java.lang.String[] attrs,<br />　　boolean attrsOnly)</td></tr></tbody></table><p>　　1) Base: 表明从该Basedn开始搜索，可以通过MBean获取</p><p>　　2) Scope: 搜索的范围：</p><p>　　　　a) LDAPv2.SCOPE_BASE， 只搜索basedn指定的entry<br />　　　　b) LDAPv2.SCOPE_ONE, 在basedn的下一级entry中搜索，不包括basedn<br />　　　　c) LDAPv3.SCOPE_SUB，在basedn的全部下级entry中搜索，包括basedn</p><p>　　3) Filter：过虑条件。比如uid=ZHANGSAN，搜索UID为ZHANGSAN的用户；再比如uid=ZHANG*，搜索 ZHANG开头的帐号；或者uid=Z*S，搜索以Z开头S结尾的帐号。前面已经介绍过这里就不多说了。</p><p>　　4) attrs：返回该attrs数组中指定的属性，比如new String[]{“uid”}，只返回属性uid，其他属性将会不在结果中返回。 一般来说我们会要求开发人员只将需要的属性返回，这样避免返回无用的属性，降低网络和Server等方面的资源开销；而且如果存在一个有较大属性集合的Entry，并且你并不使用到这个较大的属性集合。举个实际例子来说，比如你的系统中拥有很多成员数目超过2万或者更多的LDAP Groups，并且你希望通过LDAP Search找出某一个用户属于的组CN，那么搜索结果只返回组的CN已经可以满足你的要求了，这时就没有必要将全部member属性也返回了。这在后面我会有代码来说明。</p><p>　　这里还有一点很重要的细节，相信对读者会有帮助。比如，你希望搜索到modifytimestamp等Operational Attributes。这些属性（LDAP Server自己负责维护的，用户无法修改的）必须要在参数attrs中指定，LDAP Server才会返回给客户端。如果用户希望返回全部User Attributes的同时，返回指定的 Operational Attributes那该怎么办？不在attrs列表中的属性将不会被返回，一旦我指定了attrs，是否需要将全部的属性都列在attrs参数中？实际上此时只需要传入 new String[]{ “*”, “createtimestamp”}这样的参数即可；“*”号即代表了全部的User Attributes。在Sun JNDI LDAP Service Provider中也是这样，尽管找遍Sun关于JNDI的文档也找不到这样的说明。LDAP协议对此的说明在 <a href="http://www.ietf.org/rfc/rfc2251.txt" target="_blank">http://www.ietf.org/rfc/rfc2251.txt</a> 第28页。</p><p>　　5) attrsOnly：只返回属性名称，不包含值。我们一般设置为false。</p><p>　　我们下面看一下Sun JNDI中关于LDAP Search的接口：</p><p>public NamingEnumeration search(String name,<br />　　String filter,<br />　　SearchControls cons)<br />　　throws NamingException<br />name参数就是上面的basedn，SearchControls中封装更多的设置，比如：SearchControls. setSearchScope(int scope)其中的scope对应上面的2)； SearchControls.setReturningAttributes(String[] attrs)，设置指定返回的属性，对应上面的4)。</p><p>　　其他的参数还包括Size Limit，即限制搜索结果的数目（LDAP返回的Entry一般情况下是没有排序的，除非使用一些Sort Control），当你的搜索可能会返回成千上万的Entry时，限制搜索的数目是非常明智也是必须的。关于这些，读者可以参考API文档或者LDAP协议。</p><p><b>4.5.3 根据用户的Entry DN，和用户输入的口令完成身份验证<br />　　</b>下面的方法实现了到LDAP的身份验证。LDAP中的用户实际上就是一个LDAP Entry，一次验证实际是将Connection与这个Entry进行绑定，因此验证时我们需要两个必须的参数，一个是Entry的DN，一个是口令。LDAP的身份验证方式有多种，包括SASL以及基于证书的验证等，我们这里只介绍Simple Authentication。关于SASL更多信息读者可以参考：<a href="http://www.ietf.org/rfc/rfc2222.txt" target="_blank">http://www.ietf.org/rfc/rfc2222.txt</a>。</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>  /**<br />   * <br />   * dn<br />   * pwd<br />   * <br />   * LDAPException<br />   */<br />  public boolean authentication( String dn, String pwd) throws LDAPException {<br />    LDAPConnection conn= pool.getConnection();<br />    try { <br />      conn.authenticate( dn, pwd);<br />      return true;<br />    }catch ( LDAPException ex) { <br />      if ( ex.getLDAPResultCode()== LDAPException.INVALID_CREDENTIALS) { <br />        return false; // 用户口令错误 }<br />      if ( ex.getLDAPResultCode()== LDAPException.NO_SUCH_OBJECT) { <br />        return false; // 用户不存在 }<br />      throw ex; <br />    }finally { <br />      try {<br />        if ( conn!= null) pool.close(conn);<br />      }catch ( Exception ex) {<br />      } <br />    }<br />  } </td></tr></tbody></table><p><br /><b>　　</b>我们这里使用 LDAP Bind操作完成对用户的身份验证。本文前面曾提到也可以使用LDAP Compare，通过比较口令属性（userpassword）中的值来完成验证。我们需要根据不同的LDAP Server选择合适的验证方法。</p><p><b>　　</b>比如iPlanet LDAP Server 5，只能通过Bind完成认证；而Oracle Internet Directory可以通过LDAP Compare完成验证，而且还支持口令策略。</p><p><b>　　</b>如果我们使用了连接池，连接池中的连接一般都是使用权限较大的用户初始化的，这样这些连接才可以完成对LDAP的搜索操作；而当通过这些连接对普通用户进行身份验证时，如果通过验证，连接的身份将被改变为普通的用户（或称为与普通用户的身份关联）。普通用户很可能没有除了bind以外的任何权限，所以在连接被放入池中前，我们必须要恢复连接的身份。</p><p><b>　　</b>这样我们必须执行两次LDAP Bind，一次用于对普通用户验证身份；一次用于恢复连接的较大权限的用户身份。我们看到这样效率是比较低的，可能你在LDAP Server端统计有2万次bind请求，实际上只有1万人次登录。</p><p><b>　　</b>对于特定的LDAP Server，比如 Oracle Internet Directory，可以通过LDAP Compare对用户身份进行验证，并且不会改变连接关联的用户身份。这样我在使用池的情况下只需要一次LDAP Compare即可。效率有很大提高。如果不通过定制LDAP Authentication Provider，这样的调优是没法实现的。</p><p><b>　　</b>Netscape LDAP SDK的ConnectionPool的实现中，在连接放入池中前会检查连接的身份，如果身份被改变，那么会重新进行bind。所以我们没有必要在代码中再做bind。</p><p><b>4.5.4 根据用户的DN搜索用户属于的组列表<br />　　</b>有了上面的基础后，这个方法就很容易理解了。下面我们来看看如何返回用户所属的组。</p><p></p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>/**<br />   * <br />   * groupbasedn<br />   * memberDn<br />   * <br />   * LDAPException<br />   */<br />  public List getGroupMembership( String groupbasedn, String memberDn) throws LDAPException {<br />    LDAPConnection conn= pool.getConnection(); <br />    try { <br />      LDAPSearchResults sr= conn.search(<br />          groupbasedn,<br />          2,<br />          "(uniquemember="+ memberDn +")", <br />          new String[] {"cn"},<br />          false);<br />      List groups= new java.util.ArrayList();<br />      while ( sr.hasMoreElements()) {<br />        LDAPEntry entry= sr.next();<br />        LDAPAttribute attr= entry.getAttribute("cn");<br />        if ( attr!= null) {<br />          String[] values= attr.getStringValueArray(); <br />          if ( values!= null &amp;&amp; values.length&gt;0) groups.add( values[0]);<br />        }<br />      }<br />      return groups;<br />    }catch ( LDAPException ex) {<br />      throw ex;<br />    }finally { <br />      try { <br />        if ( conn!= null) pool.close(conn);<br />      }catch ( Exception ex) {<br />      }<br />    }<br />  } </td></tr></tbody></table><p><b>　　</b>成员和组的membership主要是通过uniquemember或者 member属性来定义的。成员不仅仅可以使用用户，也可以是组，因为组可以嵌套。<br /><br /><b>　　</b>a) 上面的方法中只实现了一个层次的搜索，即用户——组的搜索，而组间的嵌套搜索没有实现。读者可以根据系统内的具体情况，在此处也可以做一些优化。</p><p><b>　　</b>b) 组成员的数量可能比较大，为了避免不必要的开销，我们指定只返回组的cn属性。</p><p><b>　　</b>c) 动态组的优化。在WebLogic默认的实现中，Provider（实际为LoginModule）会不断的拿动态组中定义的URL中的filter和用户的DN去LDAP做搜索，来看用户是否属于该组。本文前面也讨论了，这样效率很低，完全可以放在Provider本地实现URL和Entry的匹配。</p><p><b>4.5.5 LoginModule中的login()方法实现<br />　　</b>一切准备就绪后，我们就可以完成LoginModule.login()这个最核心的方法了。下面我根据代码中的注释逐条说明。</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>  // <b>A</b><br />  boolean loginSucceeded;<br />  // <b>B</b><br />  List principals= new java.util.ArrayList();<br />  /**<br />   *<br />   * <br />   * LoginException <br />   */<br />  public boolean login() throws LoginException { <br />    // <b>C</b><br />    Callback[] callbacks= new Callback[] { <br />        new NameCallback("username: "),<br />        new PasswordCallback("password: ",false)};<br />    try { <br />      callbackHandler.handle( callbacks);<br />    }catch (IOException e) {<br />      throw new LoginException(e.toString());<br />    }catch (UnsupportedCallbackException e) {<br />      throw new LoginException(e.toString() + " " +e.getCallback().toString());     }<br />    //<br />    String userName = ((NameCallback)callbacks[0]).getName(); <br />    if ( userName== null || userName.length()== 0) {<br />      throw new LoginException("User login name is empty!");<br />    }<br />    // <br />    PasswordCallback passwordCallback= (PasswordCallback)callbacks[1];     char[] password = passwordCallback.getPassword();<br />    passwordCallback.clearPassword();<br />    if ( password== null || password.length== 0) {<br />      throw new LoginException("User password is empty!");<br />    }<br />    try {<br />      // <b>D</b><br />      String dn= this.getUserDN( userName);<br />      if ( dn== null) {<br />        throw new LoginException("User "+ userName +" doesn't exist."); <br />      }<br />      // <b>E</b><br />      boolean authResult= this.authentication( dn, String.valueOf( password));       if ( authResult== false) {<br />        throw new FailedLoginException("User login failed.");<br />      }<br />      // <b>F</b><br />      principals.add( new WLSUserImpl( userName));<br />      // <b>G</b><br />      List groups= this.getGroupMembership( "ou=groups,o=examples", dn);       for ( int i=0, n=groups.size(); i&lt;n; i++) {<br />        String cn= String.valueOf(groups.get(i));<br />        // <b>H</b><br />        principals.add( new WLSGroupImpl(cn)); <br />      }<br />    }catch ( LDAPException ex) { <br />      java.io.StringWriter sw = new java.io.StringWriter();  <br />     ex.printStackTrace(new java.io.PrintWriter(sw));<br />      sw.flush();<br />      throw new LoginException( sw.toString());<br />    } <br />       return loginSucceeded= true;<br />  } </td></tr></tbody></table><p><b>　　</b>a) 用于保存验证结果，在后续方法（commit等）中使用<br /><br /><b>　　</b>b) 用于保存和用户关联的Principal，在后续方法（commit等）中使用<br /><br /><b>　　</b>c) 在JAAS中通过CallbackHandler来完成用户和底层安全认证系统间信息的交换，这里我们通过CallbackHandler获取用户输入的登录帐号和口令。CallbackHandler将在初始化LoginModule时由WebLogic Security Framework传入。读者可能会问，我是否可以在此要求WebLogic Security Framework传入我自己实现的CallbackHandler，进而获取更多的信息，支持更多的Callback？很遗憾，不可以。只有在一种情况下（本文前面也提到）有一个变通，就是在Web应用中，通过Form Based这种认证方式进行用户身份验证时，可以获取更多的登录表单中提交的cgi变量。</p><p><b>　　</b>下面的代码将演示这种技术：</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>    Callback[] callbacks= new Callback[] { <br />        new NameCallback("username: "),<br />        new PasswordCallback("password: ",false),<br />        new TextInputCallback("my_hidden_field")};<br />    try { <br />      callbackHandler.handle( callbacks); <br />    }catch (IOException e) {<br />      throw new LoginException(e.toString());<br />    }catch (UnsupportedCallbackException e) {<br />      throw new LoginException(e.toString() + " " +e.getCallback().toString());     } <br />    String value= ((TextInputCallback)callbacks[2]).getText(); </td></tr></tbody></table><p><b>　　</b>这样我们通过((TextInputCallback)callbacks[2]).getText()来获取表单中更多的信息。其中TextInputCallback的Prompt必须要和表单中对应域的名字一致，否则取不到信息。如果有多个域，则需要传入多个TextInputCallback。</p><p><b>　　</b>同时如果登录表单中没有这些对应TextInputCallback的域，或者使用JNDI方式验证，那么会抛出UnsupportedCallbackException。</p><p><b>　　</b>下面是一个符合Servlet安全规范的登录表单，其中域my_hidden_field的值将通过CallbackHandler传递到的TextInputCallback中：</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>&lt;html&gt;<br /><b>　　</b>&lt;form method=”POST” action=”j_security_check”&gt;<br /><b>　　</b><b>　　</b>&lt;input type=”text” name=”j_username”&gt;<br /><b>　　</b><b>　　</b>&lt;input type=”password” name=”j_password”&gt;<br /><b>　　</b><b>　　</b>&lt;input type=”hidden” name=”my_hidden_field”value=”Hello Callback!”&gt;<br /><b>　　</b>&lt;/form&gt;<br />&lt;/html&gt;</td></tr></tbody></table><p><b>　　</b>d) 生成一个用于存放Principal的集合对象<br /><b>　　</b>e) 通过登录帐号获取LDAP中的Entry DN<br /><b>　　</b>f) 通过DN和用户口令进行身份验证<br /><b>　　</b>g) 将通过验证的用户名加入到Principal列表中，这些以后都将代表用户的一定权限<br /><b>　　</b>h) 查找用户属于哪些组<br /><b>　　</b>i) 将这些组的名字加入到Principal列表中</p><p><b>4.5.6 LoginModule中的commit()和abort()方法实现<br />　　</b>完成了以上的验证后，我们需要通过LoginModule.commit()方法提交验证结果；或者abort()方法取消验证结果。</p><table cellspacing="0" cellpadding="4" width="98%" align="center" bgcolor="#e8e8e8" border="0"><tbody><tr><td>  // <b>A</b><br />  private Subject subject;<br />    /**<br />   *<br />   * <br />   * LoginException<br />   */<br />  public boolean commit() throws LoginException { if (loginSucceeded) { <br />  // <b>B</b><br />      subject.getPrincipals().addAll(principals);<br />      return true;<br />    }else { <br />      return false;<br />    } <br />  }<br />    /**<br />   * <br />   * <br />   * LoginException<br />   */<br />    public boolean abort() throws LoginException {<br />    // <b>C</b><br />    subject.getPrincipals().removeAll(principals); <br />    return true;<br />  }    </td></tr></tbody></table><p> </p><p><b>　　</b>我这里仍然通过代码中的注释进行相应的说明：</p><p><b>　　</b>a) JAAS Subject，在LoginModule初始化时，由WebLogic Security Framework负责传入，用户的权限信息（Principals等）都将封装到该Subject中。同时Principal Validator会对Subject中的Principals进行签名，防止被黑客纂改。</p><p><b>　　</b>b) 用户认证成功的情况下，将用户关联的Principals加入到Subject中。这样用户在进行后续的访问时，WebLogic Security Framework会检查与用户关联的Subject中是否有可以访问受限资源的Principal。</p><p><b>　　</b>c) 用户登录失败的情况下（有可能是其他的LoginModule导致的整体登录失败，具体参考JAAS Control Flag），我们在commit中添加的Principals必须从Subject中删除。</p><p><br /><b>　　</b>通过上面的描述和讨论，相信读者已经对如何实现一个LDAP AuthenticationProvider/LoginModule有了比较清楚的了解。限于篇幅省略了很多细节，对此有需要的读者可以根据文中给出的相关链接做进一步的了解；或者与我交流。从这里我们也可以看到核心的逻辑还是比较简单的。其中将LDAP Group作为用户的Principal，是J2EE Security与LDAP Security结合的非常重要的一步。通过这种结合，我们可以将LDAP作为后端，设计出基于J2EE，JAAS的用户身份验证，权限管理等系统。<a id="5" name="5"></a><br /><br /><b>5 部署中的注意事项</b><br /><b>　　</b>开发完成LDAP Authentication Provider后，需要将通过WebLogic MBean Maker生成的MBean Jar File放到/server/lib/mbeantypes目录下。由于WebLogic Server在启动时会加载这些Provider，因此在一个分布式的环境中，WebLogic Domain内的全部WLServer（包括Managed Server）均需要部署该Jar包。</p><p><b>　　</b>同时由于使用了Provider MBean，因此，当你删除了MBean Types中定义的，并且通过修改保存在config.xml（WebLogic Domain配置文件）中的属性时，重新部署的Jar包会导致WebLogic Server无法正常启动，因为它没有办法按照config.xml中配置的属性初始化MBean。此时需要手工修改config.xml，删除相关的属性即可。</p><p>&lt;examples.security.providers.authentication.simple.SimpleSampleAuthenticator<br /><b>　　</b>ControlFlag="OPTIONAL"<br /><b>　　</b>Name="Security:Name=myrealmSimpleSampleAuthenticator"<br /><b>　　</b>UserBaseDN="ou=people, o=examples" Realm="Security:Name=myrealm"/&gt;</p><p><b>　　</b>比如UserBaseDN已经不需要通过MBean来管理，并且在MBean Types中已经删除，那么直接删除这里的UserBaseDN="ou=people, o=examples" 就可以了。<a id="6" name="6"></a><br /><br /><b>6 结束语</b><br /><b>　　</b>本文所讨论的内容有些过于广泛，从我个人角度讲，写起来也比较困难，很多技术问题没有进行深入的讨论。其实只是希望通过本文，能够帮助读者对WebLogic Security，J2EE Security，LDAP Security以及JAAS有一个初步的认识；能够给希望实现LDAP Authentication Provider，或者被WebLogic LDAP Authentication Provider困惑的读者一些启示。只要能够对读者有所帮助，本文的目的就算达到了。</p><p><b>　　</b>限于篇幅，本文很多地方的表述可能并不清晰，也可能会有一些错误，如果在阅读过程中产生任何疑问或者有任何看法都可以通过E-mail来与我交流，我也非常希望能和大家交流。<a id="7" name="7"></a><br /><br /><b>7 参考资料</b><br />a) <a href="http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777" target="_blank">http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777</a> 关于WebLogic Resource的更多说明<br />b) <a href="http://e-docs.bea.com/wls/docs81/security/index.html" target="_blank">http://e-docs.bea.com/wls/docs81/security/index.html</a> 关于WebLogic Security的全部内容<br />c) <a href="http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542" target="_blank">http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542</a> 关于Role Mapping Provider的更多内容<br />d) <a href="http://dev2dev.bea.com.cn/techdoc/2005012102.html" target="_blank">http://dev2dev.bea.com.cn/techdoc/2005012102.html</a> 关于WebLogic Console的扩展<br />e) <a href="http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp" target="_blank">http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp</a> Custom Seuciryt Provider的Samples<br />f) <a href="http://java.sun.com/products/JavaManagement/wp/" target="_blank">http://java.sun.com/products/JavaManagement/wp/</a> 更多关于Sun JMX<br />g) <a href="http://java.sun.com/products/jndi/tutorial/ldap/connect/index.html" target="_blank">http://java.sun.com/products/jndi/tutorial/ldap/connect/index.html</a> 关于如何使用Sun JNDI LDAP Service Provider中提供的连接池<br />h) <a href="http://www.ietf.org/rfc/rfc2254.txt" target="_blank">http://www.ietf.org/rfc/rfc2254.txt</a> 更多关于LDAP Filter<br />i) <a href="http://www.ietf.org/rfc/rfc2251.txt" target="_blank">http://www.ietf.org/rfc/rfc2251.txt</a> LDAP V3协议<br />j) <a href="http://www.ietf.org/rfc/rfc2222.txt" target="_blank">http://www.ietf.org/rfc/rfc2222.txt</a> 更多关于SASL</p><!--文章其他信息--><img src ="http://www.blogjava.net/beauty_beast/aggbug/74495.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/beauty_beast/" target="_blank">柳随风</a> 2006-10-11 10:45 <a href="http://www.blogjava.net/beauty_beast/archive/2006/10/11/74495.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>weblogic  portal cluster 安装配置</title><link>http://www.blogjava.net/beauty_beast/archive/2006/04/18/41703.html</link><dc:creator>柳随风</dc:creator><author>柳随风</author><pubDate>Tue, 18 Apr 2006 10:02:00 GMT</pubDate><guid>http://www.blogjava.net/beauty_beast/archive/2006/04/18/41703.html</guid><wfw:comment>http://www.blogjava.net/beauty_beast/comments/41703.html</wfw:comment><comments>http://www.blogjava.net/beauty_beast/archive/2006/04/18/41703.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/beauty_beast/comments/commentRss/41703.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/beauty_beast/services/trackbacks/41703.html</trackback:ping><description><![CDATA[
		<p>这两天负责安装生产环境的portal 集群的安装，虽然遭遇了一些挫折，还是安装成功。<br />其实整个安装过程是非常简单的，但如果按照对应的官方文档安装实际上还是有问题的，需要调整一些参数，并且引用相关的库文件，否则整个安装过程还是比较痛苦的。因涉及到一些生产环境信息，没时间处理，故文档暂不发布,有空再更新，先留个记号。有兴趣的可以和我交流。<br />中间件版本 weblogic 8.1 SP4<br /></p>
<img src ="http://www.blogjava.net/beauty_beast/aggbug/41703.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/beauty_beast/" target="_blank">柳随风</a> 2006-04-18 18:02 <a href="http://www.blogjava.net/beauty_beast/archive/2006/04/18/41703.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>