Spring Securityを使ってLDAP認証する方法

# 概要
Spring Securityを使った認証で、裏側でLDAPを使って認証させたいときって具体的にどういうコードを書けばいいんだいっていうお話
今回認証はLDAP、権限の管理はDBで行っている。

# 具体的な話

```xml:pom.xml
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-ldap</artifactId>
  <version>3.2.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
  <version>1.0.2.RELEASE</version>
</dependency>
```

```java:LoginController.java
@Controller
public class LoginController {

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String loginInput() {
		return "login";
	}

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String login() {
		return "search";
	}

}
```

* 権限情報の設定

権限の管理はDBに保存されている情報から取得する。
権限の情報をSpringに渡すにはLdapAuthoritiesPupulatorをimplementsしたクラスを作成する。

```java:DbRoleLdapAuthoritiesPupulator
@Component
public class DbRoleLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator{

	@Autowired
	private UserRepository userRepository;

	@Override
	public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations dirContextOperations, String username) {
		List<GrantedAuthority> authorities = new ArrayList<>();
		User user = userRepository.findByName(username);
		for (Authority auth : Authority.values()) {
			if (auth.getAuthCode() == user.getAuthority()) {
				authorities.add(new SimpleGrantedAuthority(auth.name())); //auth.nameは"ADMIN"とか権限の名前が入る。ROLE_をプレフィックスにするとエラーが出た。
				break;
			}
		}
		return authorities;
	}
```
 

* 認証方法の設定

```java:WebSecurityConfig.java
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.csrf().disable();
		http.authorizeRequests().
				antMatchers("/login").permitAll().anyRequest().authenticated().and().
				formLogin().loginPage("/login").failureUrl("/login").defaultSuccessUrl("/search").and().
				logout().logoutUrl("/logout").permitAll();
	}


	@Override
	public void configure(AuthenticationManagerBuilder auth) throws Exception {
		DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://${ldap_url}:${ldap_port}");
		contextSource.afterPropertiesSet();


		LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> configure = auth.ldapAuthentication();

		configure.userDnPatterns("uid={0},ou=fuga,dc=hoge,dc=co,dc=jp").
		contextSource(contextSource).
		ldapAuthoritiesPopulator(dbRoleLdapAuthoritiesPopulator);
	}

}
```

**protected void configure(HttpSecurity http)**
このメソッドは認証には関係ない。
ただantMathcers("path").permitAll()しているURL以外(この場合/login以外)にアクセスした時にログインページへ遷移して認証が求められるようになっている。

**public void configure(AuthenticationManagerBuilder auth)**
認証設定の本命
メソッド内で呼び出されている"ou=fuga,dc=hoge,dc=co,dc=jp"などの引数は各々の環境でのLDAP設定を書いて貰えばいい。
ただ、uid={0}としているところではまった。
ここにはログインの際に入力したユーザ名が入るのだが、ログインフォームのname属性をusernameにしなければならない。そういえばpasswordもname="password"じゃないとダメな気がしてきた。

* ログインフォーム

```html:login.html
<form class="loginForm" action="/login" method="POST" name="loginForm">
  <!--nameはusername固定-->
  <input name="username" type="text">
  <input name="password" type="password">
  <input type="submit" value="ログイン">
</form>
```

---

LDAPの話と直接関係ないけど、Spring Securityを入れるとデフォルトでオンになるBasic認証は以下の設定を書けばオフにできる

```yaml:application.yml
security:
  basic:
    enabled: false
```


# 参考
http://www.slideshare.net/makingx/grails-30-spring-boot
135スライド目からSpring Securityの話