最新の安定バージョンについては、Spring Security 6.4.3 を使用してください! |
LDAP 認証
LDAP は、多くの場合、組織がユーザー情報の主要リポジトリとして、および認証サービスとして使用します。また、アプリケーションユーザーのロール情報を保存するためにも使用できます。
Spring Security の LDAP ベースの認証は、Spring Security が認証用のユーザー名 / パスワードを受け入れるように構成されている場合に使用されます。ただし、認証にユーザー名 / パスワードを利用しているにもかかわらず、UserDetailsService
を使用して統合することはできません。これは、バインド認証で LDAP サーバーがパスワードを返さないため、アプリケーションがパスワードの検証を実行できないためです。
Spring Security の LDAP プロバイダーを完全に構成できるように、LDAP サーバーの構成方法にはさまざまなシナリオがあります。認証とロールの取得に別々の戦略インターフェースを使用し、さまざまな状況を処理するように構成できるデフォルトの実装を提供します。
前提条件
Spring Security で使用する前に、LDAP に精通している必要があります。次のリンクは、関連する概念の優れた導入と、フリーの LDAP サーバー OpenLDAP を使用してディレクトリを設定するためのガイドです: www.zytrax.com/books/ldap/ (英語) 。Java から LDAP にアクセスするために使用される JNDI API に精通していることも役立つ場合があります。LDAP プロバイダーではサードパーティの LDAP ライブラリ(Mozilla、JLDAP など)は使用していませんが、Spring LDAP が広く使用されているため、独自のカスタマイズを追加する予定がある場合は、そのプロジェクトにある程度精通していると便利です。
LDAP 認証を使用する場合、LDAP 接続プールを適切に構成することが重要です。これを行う方法を知っている未知の場合は、Java LDAP ドキュメント [Oracle] (英語) を参照できます。
組み込み LDAP サーバーのセットアップ
最初に行う必要があるのは、構成を指す LDAP サーバーがあることを確認することです。簡単にするために、多くの場合、組み込み LDAP サーバーから始めるのが最善です。Spring Security は、次のいずれかの使用をサポートします。
以下のサンプルでは、password
のパスワードを持つユーザー user
と admin
で組み込み LDAP サーバーを初期化するために、クラスパスリソースとして users.ldif
として以下を公開します。
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password
dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
組み込み UnboundID サーバー
UnboundID (英語) を使用する場合は、次の依存関係を指定します。
次に、EmbeddedLdapServerContextSourceFactoryBean
を使用して組み込み LDAP サーバーを構成できます。これにより、Spring Security はインメモリ LDAP サーバーを起動するように指示されます。
Java
Kotlin
@Bean
public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
return EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
}
@Bean
fun contextSourceFactoryBean(): EmbeddedLdapServerContextSourceFactoryBean {
return EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer()
}
または、組み込み LDAP サーバーを手動で構成することもできます。このアプローチを選択した場合は、組み込み LDAP サーバーのライフサイクルを管理する責任があります。
Java
XML
Kotlin
@Bean
UnboundIdContainer ldapContainer() {
return new UnboundIdContainer("dc=springframework,dc=org",
"classpath:users.ldif");
}
<b:bean class="org.springframework.security.ldap.server.UnboundIdContainer"
c:defaultPartitionSuffix="dc=springframework,dc=org"
c:ldif="classpath:users.ldif"/>
@Bean
fun ldapContainer(): UnboundIdContainer {
return UnboundIdContainer("dc=springframework,dc=org","classpath:users.ldif")
}
組み込み ApacheDS サーバー
Spring Security は ApacheDS 1.x を使用しますが、これは保守されなくなりました。残念ながら、ApacheDS 2.x はマイルストーンバージョンのみをリリースしており、安定版リリースはありません。ApacheDS 2.x の安定したリリースが利用可能になったら、更新を検討します。 |
Apache DS (英語) を使用する場合は、次の依存関係を指定します。
Maven
Gradle
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>1.5.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
<scope>runtime</scope>
</dependency>
depenendencies {
runtimeOnly "org.apache.directory.server:apacheds-core:1.5.5"
runtimeOnly "org.apache.directory.server:apacheds-server-jndi:1.5.5"
}
その後、組み込み LDAP サーバーを構成できます。
Java
XML
Kotlin
@Bean
ApacheDSContainer ldapContainer() {
return new ApacheDSContainer("dc=springframework,dc=org",
"classpath:users.ldif");
}
<b:bean class="org.springframework.security.ldap.server.ApacheDSContainer"
c:defaultPartitionSuffix="dc=springframework,dc=org"
c:ldif="classpath:users.ldif"/>
@Bean
fun ldapContainer(): ApacheDSContainer {
return ApacheDSContainer("dc=springframework,dc=org", "classpath:users.ldif")
}
LDAP ContextSource
構成を指す LDAP サーバーを用意したら、ユーザーの認証に使用する必要がある LDAP サーバーを指すように Spring Security を構成する必要があります。これは、JDBC DataSource
と同等の LDAP ContextSource
を作成することによって行われます。EmbeddedLdapServerContextSourceFactoryBean
をすでに構成している場合、Spring Security は、組み込み LDAP サーバーを指す LDAP ContextSource
を作成します。
Java
Kotlin
@Bean
public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =
EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
contextSourceFactoryBean.setPort(0);
return contextSourceFactoryBean;
}
@Bean
fun contextSourceFactoryBean(): EmbeddedLdapServerContextSourceFactoryBean {
val contextSourceFactoryBean = EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer()
contextSourceFactoryBean.setPort(0)
return contextSourceFactoryBean
}
または、提供された LDAP サーバーに接続するように LDAP ContextSource
を明示的に構成することもできます。
Java
XML
Kotlin
ContextSource contextSource(UnboundIdContainer container) {
return new DefaultSpringSecurityContextSource("ldap://localhost:53389/dc=springframework,dc=org");
}
<ldap-server
url="ldap://localhost:53389/dc=springframework,dc=org" />
fun contextSource(container: UnboundIdContainer): ContextSource {
return DefaultSpringSecurityContextSource("ldap://localhost:53389/dc=springframework,dc=org")
}
認証
Spring Security の LDAP サポートは UserDetailsService を使用しません。これは、LDAP バインド認証では、クライアントがパスワードまたはパスワードのハッシュバージョンを読み取ることができないためです。これは、Spring Security がパスワードを読み取って認証する方法がないことを意味します。
このため、LDAP サポートは LdapAuthenticator
インターフェースを使用して実装されます。LdapAuthenticator
は、必要なユーザー属性を取得するロールも果たします。これは、属性のアクセス許可が使用されている認証の種類に依存する可能性があるためです。例: ユーザーとしてバインドする場合は、ユーザー自身の権限で読み取る必要がある場合があります。
Spring Security には 2 つの LdapAuthenticator
実装が提供されています。
バインド認証の使用
バインド認証 (英語) は、LDAP でユーザーを認証するための最も一般的なメカニズムです。バインド認証では、ユーザーの資格情報(つまり、ユーザー名 / パスワード)が LDAP サーバーに送信され、そこで認証されます。バインド認証を使用する利点は、ユーザーのシークレット(つまり、パスワード)をクライアントに公開する必要がないことです。これにより、ユーザーの漏洩を防ぐことができます。
バインド認証構成の例を以下に示します。
Java
XML
Kotlin
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
<ldap-authentication-provider
user-dn-pattern="uid={0},ou=people"/>
@Bean
fun authenticationManager(contextSource: BaseLdapPathContextSource): AuthenticationManager {
val factory = LdapBindAuthenticationManagerFactory(contextSource)
factory.setUserDnPatterns("uid={0},ou=people")
return factory.createAuthenticationManager()
}
この単純な例では、指定されたパターンでユーザーのログイン名を置き換え、そのユーザーとしてログインパスワードをバインドしようとすることで、ユーザーの DN を取得します。すべてのユーザーがディレクトリ内の単一ノードに保存されている場合、これは問題ありません。代わりに、ユーザーを見つけるために LDAP 検索フィルターを構成する場合は、次を使用できます。
Java
XML
Kotlin
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserSearchFilter("(uid={0})");
factory.setUserSearchBase("ou=people");
return factory.createAuthenticationManager();
}
<ldap-authentication-provider
user-search-filter="(uid={0})"
user-search-base="ou=people"/>
@Bean
fun authenticationManager(contextSource: BaseLdapPathContextSource): AuthenticationManager {
val factory = LdapBindAuthenticationManagerFactory(contextSource)
factory.setUserSearchFilter("(uid={0})")
factory.setUserSearchBase("ou=people")
return factory.createAuthenticationManager()
}
パスワード認証を使用する
パスワードの比較とは、ユーザーが指定したパスワードとリポジトリに保存されているパスワードを比較することです。これは、パスワード属性の値を取得してローカルで確認するか、指定されたパスワードを比較のためにサーバーに渡して実際のパスワード値が取得されない LDAP「比較」操作を実行することで実行できます。パスワードがランダムソルトで適切にハッシュされている場合、LDAP 比較は実行できません。
Java
XML
Kotlin
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, NoOpPasswordEncoder.getInstance());
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
<ldap-authentication-provider
user-dn-pattern="uid={0},ou=people">
<password-compare />
</ldap-authentication-provider>
@Bean
fun authenticationManager(contextSource: BaseLdapPathContextSource?): AuthenticationManager? {
val factory = LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, NoOpPasswordEncoder.getInstance()
)
factory.setUserDnPatterns("uid={0},ou=people")
return factory.createAuthenticationManager()
}
いくつかのカスタマイズを含むより高度な構成を以下に示します。
Java
XML
Kotlin
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, new BCryptPasswordEncoder());
factory.setUserDnPatterns("uid={0},ou=people");
factory.setPasswordAttribute("pwd"); (1)
return factory.createAuthenticationManager();
}
<ldap-authentication-provider
user-dn-pattern="uid={0},ou=people">
<password-compare password-attribute="pwd"> (1)
<password-encoder ref="passwordEncoder" /> (2)
</password-compare>
</ldap-authentication-provider>
<b:bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
@Bean
fun authenticationManager(contextSource: BaseLdapPathContextSource): AuthenticationManager {
val factory = LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, BCryptPasswordEncoder()
)
factory.setUserDnPatterns("uid={0},ou=people")
factory.setPasswordAttribute("pwd") (1)
return factory.createAuthenticationManager()
}
1 | パスワード属性を pwd として指定します |
LdapAuthoritiesPopulator
Spring Security の LdapAuthoritiesPopulator
は、ユーザーに返される権限を決定するために使用されます。
Java
XML
Kotlin
@Bean
LdapAuthoritiesPopulator authorities(BaseLdapPathContextSource contextSource) {
String groupSearchBase = "";
DefaultLdapAuthoritiesPopulator authorities =
new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
authorities.setGroupSearchFilter("member={0}");
return authorities;
}
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource, LdapAuthoritiesPopulator authorities) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
factory.setLdapAuthoritiesPopulator(authorities);
return factory.createAuthenticationManager();
}
<ldap-authentication-provider
user-dn-pattern="uid={0},ou=people"
group-search-filter="member={0}"/>
@Bean
fun authorities(contextSource: BaseLdapPathContextSource): LdapAuthoritiesPopulator {
val groupSearchBase = ""
val authorities = DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase)
authorities.setGroupSearchFilter("member={0}")
return authorities
}
@Bean
fun authenticationManager(
contextSource: BaseLdapPathContextSource,
authorities: LdapAuthoritiesPopulator): AuthenticationManager {
val factory = LdapBindAuthenticationManagerFactory(contextSource)
factory.setUserDnPatterns("uid={0},ou=people")
factory.setLdapAuthoritiesPopulator(authorities)
return factory.createAuthenticationManager()
}
Active Directory
Active Directory は独自の非標準の認証オプションをサポートしており、通常の使用パターンは標準の LdapAuthenticationProvider
にはあまり合いません。通常、認証は、LDAP 識別名を使用するのではなく、ドメインユーザー名(user@domain
形式)を使用して実行されます。これを簡単にするために、Spring Security には、一般的な Active Directory セットアップ用にカスタマイズされた認証プロバイダーがあります。
ActiveDirectoryLdapAuthenticationProvider
の構成は非常に簡単です。ドメイン名と、サーバーのアドレス [ 1 ] を提供する LDAP URL を提供するだけです。以下に設定例を示します。
Java
XML
Kotlin
@Bean
ActiveDirectoryLdapAuthenticationProvider authenticationProvider() {
return new ActiveDirectoryLdapAuthenticationProvider("example.com", "ldap://company.example.com/");
}
<bean id="authenticationProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="example.com" />
<constructor-arg value="ldap://company.example.com/" />
</bean>
@Bean
fun authenticationProvider(): ActiveDirectoryLdapAuthenticationProvider {
return ActiveDirectoryLdapAuthenticationProvider("example.com", "ldap://company.example.com/")
}