如何在没有 DAO 的情况下使用 Spring LDAP 连接到多个 url?

Posted

技术标签:

【中文标题】如何在没有 DAO 的情况下使用 Spring LDAP 连接到多个 url?【英文标题】:How to use Spring LDAP to connect to multiple urls without DAOs? 【发布时间】:2014-09-01 01:25:49 【问题描述】:

目前我有一个LDAP管理系统,使用Spring LDAP连接LDAP服务器并进行管理;但是,如果我想更改为不同的服务器,我必须关闭系统,更改配置设置,然后重新启动它。如果我可以简单地有一个允许我在不同服务器之间交换的下拉菜单,那会简单得多。

因此,我正在研究动态设置上下文源的可能性。我找到了Incorrect injection between beans,那里的答案看起来像是我希望能够完成的事情。但是,我的系统没有使用 DAO,而是使用 LdapRepository 来管理用户。

我的问题是:如何更改 Spring 使用的 ContextSource 以在运行时与 LdapRepository 类进行交互,而不是在 xml 文件中设置,同时尽可能保持我当前的项目结构?

我不想将所有东西都转换为使用 DAO,而是让这样的功能与现有代码一起工作。

编辑:还应该提到我也让 Spring 引导存储库的实现。

【问题讨论】:

您的潜在服务器列表是静态的,还是您希望能够动态添加一个新的? @SergeBallesta 它们都是静态的。目前我已经在 LdapTemplate 中启用了通过自动装配进行切换,但是我希望这是会话本地的(所以一个人交换到另一台服务器不会导致每个人都切换)。 在会话中限定您在 LdapTemplate 中自动装配的 bean 是否足够? @Serge 不幸的是,没有。我不是 100% 确定如何实现 LDAPTemplate 和 LDAPRepository 类之间的交互。据我所知,它们都引用同一个 LDAP 模板,而我希望该连接是本地会话,因此我可以执行 session.setLdapTemplate(currentLdapTemplate); 之类的操作,以便在有人切换服务器时创建一个新的 LdapTemplate。 【参考方案1】:

我发现 SO 上的其他 post(与您的问题没有直接关系)显示了如何在会话中限定 LDAPTemplate

在不了解您的更多来源的情况下,我无法确定此解决方案是否适合您,但我认为值得一试。

您应该在会话中声明您的 LDAPTemplate 范围:

<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate" 
      p:contextSource_ref="contextSource" scope="session">
    <aop:scope-proxy/>
</bean>

您应该尝试将其作为 LDAPOperation 注入到您的 LDAPRepository 类中,以允许 Spring AOP 使用 JDK 代理。如果不行,你应该通过&lt;aop:scope-proxy proxy-target-class="true"/&gt;配置Spring AOP使用cglib

在您要更改ContextSource 的地方,注入LDAPTemplate。如果您可以使用 JDK 代理,请将其作为 LDAPOperation 注入以获取作用域代理。您应该能够通过 Spring AOP 代理实现的Advisedinterface 获得正确的LDAPTemplate

LDAPOperation ldapOperation;
...
LDAPTemplate ldapTemplate = (LDAPTemplate) ((Advised)ldapOperation)
                                             .getTargetSource().getTarget();
ldapTemplate.setContextSource(newContextSource);

这里是访问 Spring AOP 代理目标的参考:来自 Tech Per 的 How To Acess Target Object Behind a Spring Proxy

【讨论】:

所有 LdapRepositories 默认使用相同的LdapTemplate。在调用之前显式设置ContextSource 在多线程环境中会产生未定义的结果。 @marthursson 这就是我建议每个 Http 会话使用一个 LdapTemplate 的原因。【参考方案2】:

这是一种极端情况。该库实际上不是为此而设计的,但我认为实现您想要做的最简单的方法是实现一个自定义委托ContextSource,它保留对您希望能够的所有不同实际 ContextSources 的引用使用,例如:

public class SessionBasedDelegatingContextSource implements ContextSource 
  private Map<String, ContextSource> contextSources;

  @Required
  public void setContextSources(Map<String, ContextSource> contextSources) 
    this.contextSources = new HashMap<>(contextSources);
  

  protected final ContextSource getSessionContextSource() 
    String id = (String) RequestContextHolder.currentRequestAttributes()
                 .getAttribute("currentContextSource", SCOPE_SESSION);

    if(id == null) 
      throw new IllegalStateException("No Ldap target selected");
    

    ContextSource contextSource = contextSources.get(id);
    if(contextSource == null) 
      throw new IllegalArgumentException("No Ldap target selected");
    

    return contextSource;
  

  @Override
  public DirContext getReadOnlyContext() 
    getSessionContextSource().getReadOnlyContext();
  

  @Override
  public DirContext getReadWriteContext() 
    getSessionContextSource().getReadWriteContext();
  

  @Override
  public DirContext getContext(String principal, String credentials) 
    getSessionContextSource().getContext(principal, credentials);
  

现在,如果您注册一个RequestContextFilter,声明所有不同的 ContextSource,将它们注入SessionBasedDelegatingContextSource,并将其用作您的存储库的 ContextSource(即您定义的 LdapTemplate 使用的 ContextSource),您只需要要做的是在会话中放置一个适当的 ContextSource 标识符,你应该没问题。

【讨论】:

这几乎正是我最终所做的。谢谢!

以上是关于如何在没有 DAO 的情况下使用 Spring LDAP 连接到多个 url?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot下如何自定义Repository中的DAO方法

Spring Web 开发中的通用 DAO

是否可以在没有 Dagger 模块的情况下提供 DAO 或 Room 数据库

在没有父POM的情况下使用Spring Boot

不使用 Spring 获取 EntityManager

java中如何调用DAO