使用Spring Security获取通过Active Directory登录的用户的电子邮件地址

5

我正在开发一个基于Spring MVC的Web应用程序,使用Spring Security允许用户使用他们的Active Directory凭据登录。我想在用户登录后获取其电子邮件地址。我已经使登录功能正常运行,并且现在正在尝试找出如何获取用户的电子邮件地址。看起来我应该从一个InetOrgPerson对象中获取它。我已经尝试在XML配置文件中指定一个InetOrgPersonContextMapper

<bean id="ldapActiveDirectoryAuthProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <constructor-arg name="domain" value="foo.bar" />
    <constructor-arg name="url" value="ldap://foo.bar" />
    <property name="useAuthenticationRequestCredentials" value="true" />
    <property name="convertSubErrorCodesToExceptions" value="true" />
    <property name="userDetailsContextMapper">
        <bean class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper" />
    </property>
</bean>

我正在尝试在控制器中获取电子邮件,代码如下:

@RequestMapping(value = "/startProcess.html", method = RequestMethod.POST)
public ModelAndView startProcess(@ModelAttribute Token token, Principal principal)
{
    ModelAndView mav = new ModelAndView();

    String user = principal.getName();
    String email = ((InetOrgPerson)principal).getMail();

    logger.info("Got email \"" + email + "\" for user \"" + user + "\"");

    ...
}

这个问题导致了一个类转换异常,提示无法将UsernamePasswordAuthenticationToken转换为InetOrgPerson。我决定尝试手动获取Principal对象,而不是让Spring注入它:

@RequestMapping(value = "/startProcess.html", method = RequestMethod.POST)
public ModelAndView startProcess(@ModelAttribute Token token, Principal principal)
{
    ModelAndView mav = new ModelAndView();

    String user = principal.getName();

    Object p = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String email = ((InetOrgPerson)p).getMail();

    logger.info("Got email \"" + email + "\" for user \"" + user + "\"");

    ...
}

这告诉我它不能将LdapUserDetailsImpl转换为InetOrgPerson。既然我使用了InetOrgPersonContextMapper,那么LdapUserDetailsImpl不应该是一个InetOrgPerson吗?


1
我理解getCredentials()的结果是一个字符串,即用户名。但您的实现不是这样吗?如果是这样,那么您需要直接查询Active Directory以获取该用户名对应人员的电子邮件地址。 - Bob Dalgleish
哎呀,我复制错了代码片段。那应该是 getPrincipal() - JDiPierro
再次强调,getPrincipal() 返回的是一个字符串。你不能有效地将该字符串用作 InetOrgPerson - Bob Dalgleish
我必须不同意你的看法,因为异常告诉我getPrincipal返回一个UsernamePasswordAuthenticationToken对象,而我必须调用principal.getName()来获取用户名。据我所知,String对象没有getName函数。 - JDiPierro
3个回答

3

首先,您需要将映射器更改为org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper

<beans:bean id="inetOrgPersonContextMapper" class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper">
</beans:bean>

然后在您的身份验证提供程序中使用它

<beans:bean id="adAuthenticationProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <beans:constructor-arg value="mydomain.com.local" />
    <beans:constructor-arg value="ldap://servername/" />
    <beans:property name="userDetailsContextMapper" ref="inetOrgPersonContextMapper" />
</beans:bean>   

然后您将能够将您的主体转换为InetOrgPerson

InetOrgPerson userDetails = (InetOrgPerson)(SecurityContextHolder.getContext().getAuthentication().getPrincipal());

非常感谢!“更改映射器”就是我需要解决相同问题的方法。 - Ruslan Gabbazov

1

我忽视了一个事实,就是我正在使用自定义的 AuthenticationSuccessHandler 并在用户登录后用 UsernamePasswordAuthenticationToken 覆盖 Authentication

我将 InetOrgPersonContextMapper 保留在 ActiveDirectoryLdapAuthenticationProvider 上,在我的 AuthenticationSuccessHandler 中,我将 Principal 强制转换为 InetOrgPerson 并将其存储为 UsernamePasswordAuthenticationToken 中的 Principal。之后,在控制器中我成功将 Principal 强制转换为 InetOrgPerson 并调用 getMail()


0

希望这个解决方案适用于你的情况。 您可以使用LdapTemplate检索由CustomMapper指定的属性,即InetOrgPersonContextMapper类。

  1. 创建一个 LdapTemplate 类的 bean

    <bean id="ldapTemplate" class="com.springframework.ldap.core.LdapTemplate">
    <constructor-arg ref="contextSource" />
    </bean>

    其中 contextSource 是对 LdapContextSource 实例的引用

  2. 通过自动装配将 ldapTemplate bean 注入到 Controller 中
    @Autowire LdapTemplate ldapTemplate;

  3. 将 Principal 实例转换为 LdapUserDetailsImpl 
    LdapUserDetailsImpl ldapUserDetails = (LdapUserDetailsImpl) p;

  4. 获取用户 DN
    String dn= ldapUserDetail.getDn()

  5. 使用 ldapTemplate 实例的 Object lookup(String,ContextMapper) 方法来获取映射对象(由 mapper 映射),即 InetOrgPerson
    InetOrgPerson person=ldapTemplate.lookup(dn,new InetOrgPersonContextMapper());

  6. 享受吧!

References:
https://today.java.net/pub/a/today/2006/04/18/ldaptemplate-java-ldap-made-simple.html#searches
http://docs.spring.io/spring-ldap/docs/current/reference/htmlsingle/


我不认为我可以使用这种方法,因为我正在使用 ActiveDirectoryLdapAuthenticationProvider。文档指定在使用 ActiveDirectoryLdapAuthenticationProvider 时不需要 ContextSource - JDiPierro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接