我从JSF页面调用了一个Java doLogin()
方法,该方法从用户那里获取id(String netId
)和密码(String password
)。doLogin()
在Active Directory登录中使用netId
作为主体来启动身份验证。之后,我想从保护我的应用程序的目录中获得除主体名称之外的其他属性。
我的安全配置在容器&它起作用,这样
HttpSession ses = FacesContext.getCurrentInstance().getExternalContext().getSession (false);
HttpServletRequest req = FacesContext.getCurrentInstance().getExternalContext().getRequest();
req.login(netID, password);
成功并且
req.getUserPrincipal().getName();
返回用户的CCD_ 6。但是,我的应用程序仅将netId
用于身份验证。访问另一个数据库的应用程序的其他部分需要其他属性(例如commonName
)。我想做一些类似的事情
usefulLDAPobj = *getLDAPSession from "somewhere" in the HTTP Session, the FacesContext or some other available object*
String cn = usefulLDAPobj.getAttributeFromProfile ("cn");
ses.setAttribute("username", cn);
从那时起,在我的Hibernate ORM中使用存储在会话中的username。
我知道头脑简单的usefulLDAPobj.getAttributeFromProfile ("cn")
会更复杂,但如果我能找到一个可以访问LDAP目录的起点,我就可以填写它。
由于容器正在建立一个明显的LDAP连接,我觉得必须有一种方法让我使用它,而不必手动用程序建立LdapContext;这将需要代码知道web服务器(JBoss EAP 6.2)已经知道的所有LDAP server / bind-DN / bind-password configuration
(从CCD_ 12中定义的CCD_。例如,像getUserPrincipal()
和isUserInRole()
这样的方法需要访问我想要访问的目录配置文件
所以我的问题是:有没有一种方法可以从FacesContext或HTTPServlet请求或HTTPServlet可访问的任何对象中获取LDAP连接或上下文?
从FacesConext获取LdapConext的最简单方法是什么?
根本没有办法,更不用说简单的办法了。JSF不假定存在LDAP服务器,也不提供任何与LDAP相关的API。
由于容器正在建立一个明显的LDAP连接
当你登录时。不是永久性的。如果有LDAP服务器的话。JSF不知道容器是如何让你登录的
我觉得一定有办法。。。
没有。
我认为这个问题的一个有用答案是没有办法直接从FacesContext
获得LDAPContext
,但通过编写容器特定的登录模块和Principal
类,您可以通过FacesContext
获得的HttpServletRequest
传递额外的数据。
我将把我的解决方案的详细信息放在这里,因为尽管它与FacesContext
没有直接关系,但它提供了我在问题正文中所要求的内容,这是一种从LDAP配置文件中获取额外用户数据的方法,同时避免了创建一个完全独立的LDAPContext
的需要。
我特别想要的是CN
,我可以在不进行额外搜索的情况下从DN
中解析它。如果我需要任何其他数据,我假设我可以使用下面的findUserDN()
中的ctx
来获得。
我想我用这个解决方案让我的应用程序依赖于JBoss
,如果这是不可取的,我会搜索一个独立于JBoss
的登录模块类来扩展(不知道这是容易的、困难的还是不可能的)。
这是我的解决方案:
-
覆盖AdvancedADLoginModule 中的findUserDN(LdapContext ctx)
package ca.mycompany.myapp.jboss; import java.security.Principal; import javax.naming.ldap.LdapContext; import javax.security.auth.login.LoginException; import org.jboss.security.negotiation.AdvancedADLoginModule; public class NameFetchingADLoginModule extends AdvancedADLoginModule @Override protected String findUserDN(LdapContext ctx) throws LoginException { String lclUserDN = super.findUserDN(ctx); Principal principal = getIdentity(); if (principal instanceof PrincipalWithDisplayName) { String displayName = lclUserDN.substring(3, lclUserDN.indexOf(',')); ((PrincipalWithDisplayName) principal).setDisplayName (displayName); } return lclUserDN; } }
-
扩展Principal以提供displayName属性
package ca.mycompany.myapp.jboss; import java.io.Serializable; import java.security.Principal; public class PrincipalWithDisplayName implements Serializable, Principal { private static final long serialVersionUID = 1L; private final String name; // additional attribute provided by this subclass private String displayName; public PrincipalWithDisplayName(final String name) { this.name = name; } // new and overriding getters and setters, equals() and hashCode() removed for brevity }
-
在doLogin()方法中使用新的登录模块和Principal
代码段:
String displayName = "";
HttpSession ses = FacesContext.getCurrentInstance().getExternalContext().getSession (false);
HttpServletRequest req = FacesContext.getCurrentInstance().getExternalContext().getRequest();
try {
req.login(userName, password); // this throws an exception if authentication fails
Principal lclUser = req.getUserPrincipal();
if (lclUser instanceof PrincipalWithDisplayName)
{
displayName = ((PrincipalWithDisplayName) lclUser).getDisplayName ();
}
// get Http Session and store username
//
HttpSession session = HttpUtil.getSession();
sess.setAttribute("username", displayName);
...
- 在
standalone.xml
中配置JBoss EAP 6.2
以使用新类
代码段:
<subsystem xmlns="urn:jboss:domain:security:1.2">
<security-domains>
<security-domain name="company_ad" cache-type="default">
<authentication>
<login-module code="ca.mycompany.myapp.jboss.NameFetchingADLoginModule" flag="required">
<module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
<module-option name="java.naming.provider.url" value="ldap://servernm.mycompany.tst:389"/>
<module-option name="java.naming.security.authentication" value="simple"/>
<module-option name="bindDN" value="CN=AuthGuy,OU=Accounts,OU=Company User Accounts,DC=company,DC=tst"/>
<module-option name="bindCredential" value="Snowden1"/>
<module-option name="baseCtxDN" value="OU=Company User Accounts,DC=company,DC=tst"/>
<module-option name="baseFilter" value="(sAMnetID={0})"/>
<module-option name="searchScope" value="SUBTREE_SCOPE"/>
<module-option name="allowEmptyPassword" value="false"/>
<module-option name="rolesCtxDN" value="OU=Company User Accounts,DC=company,DC=tst"/>
<module-option name="roleFilter" value="(sAMAccountName={0})"/>
<module-option name="roleAttributeID" value="memberOf"/>
<module-option name="roleAttributeIsDN" value="true"/>
<module-option name="roleNameAttributeID" value="cn"/>
<module-option name="recurseRoles" value="1"/>
<module-option name="principalClass" value="ca.mycompany.myapp.jboss.PrincipalWithDisplayName"/>
</login-module>
</authentication>
</security-domain>
</security-domains>
</subsystem>