从 FacesContext 获取 LDAPContext 的最简单方法是什么?
What is the easiest way to get an LDAPContext from a FacesContext?
我有一个从 JSF 页面调用的 Java doLogin()
方法,该方法从用户那里获取 ID (String netId
) 和密码 (String password
)。 doLogin()
使用 netId
作为 Active Directory 登录中的主体启动身份验证。之后,我想从保护我的应用程序的目录中获取主体名称以外的其他属性。
我的安全性是在容器中配置的并且可以正常工作,因此
HttpSession ses = FacesContext.getCurrentInstance().getExternalContext().getSession (false);
HttpServletRequest req = FacesContext.getCurrentInstance().getExternalContext().getRequest();
req.login(netID, password);
成功并且
req.getUserPrincipal().getName();
returns 用户的 netID
。但是,我的应用程序仅使用 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 中使用存储在会话中的用户名。
我知道头脑简单的人 usefulLDAPobj.getAttributeFromProfile ("cn")
会更复杂,但如果我能找到一个让我访问 LDAP 目录的起点,我可以填写它。
由于容器正在设置一个明显的 LDAP 连接,我觉得 必须 是我使用它而无需手动构建 LdapContext 的一种方式以编程方式;这将要求代码知道 Web 服务器 (JBoss EAP 6.2) 已经知道的所有 LDAP server / bind-DN / bind-password configuration
(来自 standalone.xml
中定义的 <login-module>
)。例如,getUserPrincipal()
和 isUserInRole()
等方法需要访问我想要访问的同一个目录配置文件。
所以我的问题是:有没有办法从 FacesContext 或 HTTPServletRequest 或任何可从 HTTPServlet 访问的对象获取 LDAP 连接或上下文?
What is the easiest way to get an LdapConext from a FacesConext?
根本就没有办法,更别说简单的方法了。 JSF 不假定存在 LDAP 服务器,也不提供任何与 LDAP 相关的 API。
Since there is an obvious LDAP connection being set up by the container
当您登录时。不是永久的。如果根本没有 LDAP 服务器。而且 JSF 不知道容器是如何登录的。
I feel there must be a way ...
没有。
我认为这个问题的一个有用答案是 没有办法直接从 FacesContext
获得 LDAPContext
,而是通过编写特定于容器的登录模块和 Principal
class 您可以通过 HttpServletRequest
传递额外的数据,这些数据是通过 FacesContext
.
获得的
我将在此处提供我的解决方案的详细信息,因为即使它与 FacesContext
没有直接关系,它也提供了我在问题正文中要求的内容,这是获得更多信息的一种方式来自 LDAP 配置文件的用户数据,同时避免需要创建一个完整的单独 LDAPContext
。
我特别想要的是 CN
,我能够从 DN
中解析出来,而无需进行额外的搜索。如果我需要任何其他数据,我假设我可以使用下面 findUserDN()
中的 ctx
获得。
我想我正在使用此解决方案使我的应用程序依赖于 JBoss
,如果这不是我们想要的,我会搜索一个 JBoss
独立的登录模块 class 来扩展(不知道那是容易、困难还是不可能。
这是我的解决方案:
覆盖 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
,以使用新的 classes
片段:
<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>
我有一个从 JSF 页面调用的 Java doLogin()
方法,该方法从用户那里获取 ID (String netId
) 和密码 (String password
)。 doLogin()
使用 netId
作为 Active Directory 登录中的主体启动身份验证。之后,我想从保护我的应用程序的目录中获取主体名称以外的其他属性。
我的安全性是在容器中配置的并且可以正常工作,因此
HttpSession ses = FacesContext.getCurrentInstance().getExternalContext().getSession (false);
HttpServletRequest req = FacesContext.getCurrentInstance().getExternalContext().getRequest();
req.login(netID, password);
成功并且
req.getUserPrincipal().getName();
returns 用户的 netID
。但是,我的应用程序仅使用 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 中使用存储在会话中的用户名。
我知道头脑简单的人 usefulLDAPobj.getAttributeFromProfile ("cn")
会更复杂,但如果我能找到一个让我访问 LDAP 目录的起点,我可以填写它。
由于容器正在设置一个明显的 LDAP 连接,我觉得 必须 是我使用它而无需手动构建 LdapContext 的一种方式以编程方式;这将要求代码知道 Web 服务器 (JBoss EAP 6.2) 已经知道的所有 LDAP server / bind-DN / bind-password configuration
(来自 standalone.xml
中定义的 <login-module>
)。例如,getUserPrincipal()
和 isUserInRole()
等方法需要访问我想要访问的同一个目录配置文件。
所以我的问题是:有没有办法从 FacesContext 或 HTTPServletRequest 或任何可从 HTTPServlet 访问的对象获取 LDAP 连接或上下文?
What is the easiest way to get an LdapConext from a FacesConext?
根本就没有办法,更别说简单的方法了。 JSF 不假定存在 LDAP 服务器,也不提供任何与 LDAP 相关的 API。
Since there is an obvious LDAP connection being set up by the container
当您登录时。不是永久的。如果根本没有 LDAP 服务器。而且 JSF 不知道容器是如何登录的。
I feel there must be a way ...
没有。
我认为这个问题的一个有用答案是 没有办法直接从 FacesContext
获得 LDAPContext
,而是通过编写特定于容器的登录模块和 Principal
class 您可以通过 HttpServletRequest
传递额外的数据,这些数据是通过 FacesContext
.
我将在此处提供我的解决方案的详细信息,因为即使它与 FacesContext
没有直接关系,它也提供了我在问题正文中要求的内容,这是获得更多信息的一种方式来自 LDAP 配置文件的用户数据,同时避免需要创建一个完整的单独 LDAPContext
。
我特别想要的是 CN
,我能够从 DN
中解析出来,而无需进行额外的搜索。如果我需要任何其他数据,我假设我可以使用下面 findUserDN()
中的 ctx
获得。
我想我正在使用此解决方案使我的应用程序依赖于 JBoss
,如果这不是我们想要的,我会搜索一个 JBoss
独立的登录模块 class 来扩展(不知道那是容易、困难还是不可能。
这是我的解决方案:
覆盖 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
,以使用新的 classes
片段:
<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>