Spring Security データベースの認証

###データベース認証の流れ
1.ProviderManagerはフィルターからの依頼を受ける
2.ProviderManagerはDaoAuthenticationProviderに認証処理を任せる
3.DaoAuthenticationProviderはUserDetailsServiceインターフェイス(電話)にユーザー情報を持って来いと電話をかける
4.UserDetailsServiceの実装クラスのMyUserDetailsServiceは電話をとりデータベースからユーザー情報を持ってくる
5.MyUserDetailsServiceはMyUserDetailsにユーザー情報を教えてUserDetailsを作るように指示する
6.MyUserDetailsが作成したユーザー情報を受け取ったDaoAuthenticationProviderはクライアントから入力された情報とを見比べる
7.間違っていれば「違うぞ！」と声を発する

#####UserDetails
ユーザー名とパスワード、ユーザーの情報を持っておくための書類

以下のメソッドが定義されている
######UserDetailsインターフェイス

    public interface UserDetails extends Serializable {
        String getUsername();
        String getPassword();
        boolean isEnabled();
        boolean isAccountNonLocked();
        boolean isAccountNonExpired();
        boolean isCreditialsNonExpired();
        Collection<? extends GrantedAuthority> getAuthorities();
    }

getUsernameはユーザー名を返却する
getPasswordは登録されているパスワードを返却する
返却値と入力値を比べる
isEnabledはユーザーが有効かを判定
isAccountNonLockedはアカウントロックがされていないかを判定
isAccountNonExpiredはアカウントの有効期限を判定
isCreditialsNonExpiredは資格の有効期限を判定
getAuthoritiesは認可リストを返却する

###UserDetailsの実装クラス

    public class AccountUserDetails implements UserDetails {
      
      private final Account account;
      private final Collection<GrantedAuthority> authorities;

      public AccountUserDetails(
          Account account, Collection<GrantedAuthority> authorities) {
          this.account = account;
          this.authorities = authorities;
      }

      public String getPassword() {
          return account.getPassword();
      }
      public String getUsername() {
          return account.getUsername();
      }
      public boolean isEnabled() {
          return account.isEnabled();
      }
      public Collection<? extends GrantedAuthority> getAuthorities() {
          return authorities;
      }

      public boolean isAccountNonExpired() {
          return true;
      }
      public boolean isAccountNonLocked() {
          return true;
      }
      public boolean isCredentialsNonExpired() {
          return true;
      }

      public Account getAccount() {
          return account;
      }
    }


Account accountがユーザー情報
Collection<GrantedAuthority> authoritiesが認可情報
isAccountNonExpired,isAccountNonLocked,isCredentialsNonExpiredはすべてOKを返す仕様
getAccountアカウント情報へのアクセスを可能にしている

###Account
    package security.domain;

    public class Account {
    	private String username;
    	private String password;
    	private String firstName;
    	private String lastName;
    	
    	public String getUsername() {
    		return username;
    	}
    	public void setUsername(String username) {
    		this.username = username;
    	}
    	public String getPassword() {
    		return password;
    	}
    	public void setPassword(String password) {
    		this.password = password;
    	}
    	public String getFirstName() {
    		return firstName;
    	}
    	public void setFirstName(String firstName) {
    		this.firstName = firstName;
    	}
    	public String getLastName() {
    		return lastName;
    	}
    	public void setLastName(String lastName) {
    		this.lastName = lastName;
    	}
    }

###UserDetailsService
資格情報とユーザーの状態をデータベースから取得するインターフェイス
一緒にUserDetailsServiceインターフェイスを実装したクラスを作る

#####UserDetailsServiceインターフェイス
    public interface UserDetailsService {
        UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

loadUserByUsernameはUserDetailsの作り方

###UserDetailsServiceの実装クラス
		@Service
		public class AccountUserDetailsService implements UserDetailsService {
				@Autowired
				AccountRepository accountRepository;

				@Transactional(readOnly = true)
				public UserDetails loadUserByUsername(String username)
								throws UsernameNotFoundException {
						Account account = Optional.ofNullable(accountRepository.findOne(username))
										.orElseThrow(() -> new UsernameNotFoundException("user not found."));
						return new AccountUserDetails(account, getAuthorities(account));
				}

				private Collection<GrantedAuthority> getAuthorities(Account account) {
						if(account.isAdmin()) {
								return AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN");
						} else {
								return AuthorityUtils.createAuthorityList("ROLE_USER");
						}
				}
		}

Optional.ofNullable(accountRepository.findOne(username)).orElseThrow(() -> new UsernameNotFoundException("user not found."))
この分はラムダ式でOptional.ofNullableを呼び出しています
引数のaccountRepository.findOne(username)の結果がnullならば、orElseThrowが実行されてUsernameNotFoundException例外がthrowされます
次のリターン分でUserDetailsを作ります
getAuthoritiesユーザーがadminならその情報を作ります

###認証の適用
#####security-config.xml
	<sec:authentication-manager>
		<sec:authentication-provider
			user-service-ref="accountUserDetailsService">
			<sec:password-encoder ref="passwordEncoder" />
		</sec:authentication-provider>
	</sec:authentication-manager>
	
	<bean id="passwordEncoder"
		class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

AuthenticationManagerのBeanを定義します

sec:authentication-provideruser-service-ref="accountUserDetailsService"でDaoAuthenticationProviderに作成したUserDetailsServiceを指定します
DaoAuthenticationProviderにパスワードエンコーダーを設定しておきます


###まとめ
研修中の身なので、間違いなどありましたらご指摘願いたいです。