Spring SecurityのUserDetailsの挙動を確認する

##Spring Securityで認証によく使うUserDetailsについて

Spring Bootで認証を実装しようとしたとき、よく使うSpring側ですでに用意されているクラスと言えば`User`や`UserDetails`および`UserDetailsService`だと思いますが、至極当たり前のことながら意外とこれらの挙動を正しく理解していなかったので、改めて確認してみました。

##実装

まず、ユーザ情報はデータベースから取得するので、取得用のEntityを実装します。

```LoginUser.java
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name="m_user")
public class LoginUser{

    @Id
    @GeneratedValue(strategy=GenerationType.TABLE)
    @Column(name = "user_id")
    @Getter
    @Setter
    private String userId;

    @Column(name = "user_name")
    @Getter
    @Setter
    private String userName;

    @Column(name = "password")
    @Getter
    @Setter
    private String password;

    @Column(name = "pass_update_date")
    @Getter
    @Setter
    private Date passUpdateDate;

    @Column(name = "login_miss_times")
    @Getter
    @Setter
    private int loginMissTimes;

    @Column(name = "unlock")
    @Getter
    @Setter
    private boolean unlock;

    @Column(name = "enabled")
    @Getter
    @Setter
    private boolean enabled;

    @Column(name = "user_due_date")
    @Getter
    @Setter
    private Date userDueDate;

    @Column(name = "role_id")
    @Getter
    @Setter
    private String roleId;

}
```

肝心のUserDetailsです。

```UserDetailsImpl.java
import java.util.Collection;
import java.util.Date;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserDetailsImpl implements UserDetails {

	private LoginUser user;

	public UserDetailsImpl(LoginUser user) {
		this.user = user;
	}

    private Collection<? extends GrantedAuthority> authority;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authority;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUserId();
    }

    

    @Override
    public boolean isAccountNonExpired() {

        if(user.getUserDueDate().after(new Date())) {
            
            return true;

        } else {

            return false;
        }
    }

    @Override
    public boolean isAccountNonLocked() {
        return user.isUnlock();
    }

    @Override
    public boolean isCredentialsNonExpired() {

        if(user.getPassUpdateDate().after(new Date())) {

            return true;

        } else {

            return false;

        }
    }

    @Override
    public boolean isEnabled() {
        return user.isEnabled();
    }
}
```

ユーザ情報のテーブル：

```sql
select * from m_user;
 user_id |                           password                           |  pass_update_date   | login_miss_times | unlock | role_id |   user_name    | enabled |    user_due_date    
---------+--------------------------------------------------------------+---------------------+------------------+--------+---------+----------------+---------+---------------------
 system  | $2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa | 2099-12-31 23:59:59 |                0 | t      | admin   | システム管理者 | t       | 2099-12-31 23:59:59
 user    | $2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa | 2099-12-31 23:59:59 |                0 | t      | general | 一般ユーザー   | t       | 2099-12-31 23:59:59
 sample1 | $2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa | 1999-12-31 23:59:59 |                0 | t      | general | ユーザー1      | t       | 2099-12-31 23:59:59
 sample2 | $2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa | 2099-12-31 23:59:59 |                0 | f      | general | ユーザー2      | t       | 2099-12-31 23:59:59
 sample3 | $2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa | 2099-12-31 23:59:59 |                0 | t      | general | ユーザー3      | f       | 2099-12-31 23:59:59
 sample4 | $2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa | 2099-12-31 23:59:59 |                0 | t      | general | ユーザー4      | t       | 1999-12-31 23:59:59
```

この実装内容で認証した際に、有効なアカウントとして認識されるのは、`system`と`user`のみです。

というのも、`UserDetails`を実装したクラスでは、`isEnabled`や`isUnlocked`、`isAccountNonLocked`などのメソッドをオーバーライドしており、ここで`false`がひとつでも返ってきた場合は`401Unauthorized`のメッセージが返ってきて、はじかれるようになっています。

オーバーライドしている内容を見れば明らかなのですが、最終的な挙動を正しく理解していなかったので、地味に悩みました。