67604
# 質問

なぜ`UserDetailsService`を実装して、`loadUserByUsername`メソッドで username
に基づいたユーザーを返す必要があるのでしょうか？

そもそも username
の情報だけに基づいて検索して良いんでしょうか？パスワードによる認証をしている場合は、パスワードが一致していることの担保は別の機構（おそらく`HttpSecurity`で内部的に?）で行っているのでしょうか？

Spring Security側からしたら、User（正確には[principal](https://docs.spring.io/spring-
security/site/docs/5.4.0-M1/reference/html5/#servlet-authentication-
authentication)）がDBに保存されているのかインメモリに保存されているのかわからないだろうから取得方法を`loadUserByUsername`で知らせてほしいんだろうなあというのはわからなくもないですが。  

* * *

  
と、ここまで書いておいて[credentials](https://docs.spring.io/spring-
security/site/docs/5.4.0-M1/reference/html5/#servlet-authentication-
authentication)がたしかにパスワードだけとも限らないので、`loadUserByUsername`にパスワードが必要ないのも不思議ではないような気もしてきました（とはいえ、単なる推測なので、なんのために`loadUserByUsername`を実装しているのかは不明のままですが）。

[principal](https://docs.spring.io/spring-
security/site/docs/5.4.0-M1/reference/html5/#servlet-authentication-
authentication) と [credentials](https://docs.spring.io/spring-
security/site/docs/5.4.0-M1/reference/html5/#servlet-authentication-
authentication)
は2つそろうことで機能すると思うので、パスワードによる認証を行っていた場合、どのような機構で、この2つを担保しているのでしょうか？

# 参考資料

[Spring Security 使い方メモ 認証・認可 -
Qiita](https://qiita.com/opengl-8080/items/032ed0fa27a239bdc1cc)

>   * UserDetailsService を実装したクラスを作成する。
>   * UserDetailsService には loadUserByUsername(String) というメソッドが１つだけ存在する。
>     *
> 引数でユーザーを識別するための文字列（普通はログイン画面などで入力されたユーザー名）が渡ってくるので、その識別文字列に対応するユーザー情報を返却する。
>     * 対応するユーザー情報が存在しない場合は UsernameNotFoundException をスローする。
>

[UserDetailsService (spring-security-docs-manual 5.3.3.RELEASE
API)](https://docs.spring.io/spring-
security/site/docs/current/api/org/springframework/security/core/userdetails/UserDetailsService.html)

> Locates the user based on the username. In the actual implementation, the
> search may possibly be case sensitive, or case insensitive depending on how
> the implementation instance is configured. In this case, the UserDetails
> object that comes back may have a username that is of a different case than
> what was actually requested..
CC BY-SA 4.0
2020-06-12T13:25:47.860
67596
2020-06-13T08:18:23.907
2020-06-12T14:13:32.380
9008
9008
question
java spring kotlin spring-boot spring-security
UserDetailsServiceのloadUserByUsernameの存在意義がよくわからないです
回答順が前後しますが、

> username
> の情報だけに基づいて検索して良いんでしょうか？パスワードによる認証をしている場合は、パスワードが一致していることの担保は別の機構（おそらく`HttpSecurity`で内部的に?）で行っているのでしょうか？

[What is a UserDetailsService and do I need
one?](https://docs.spring.io/spring-
security/site/docs/5.3.3.RELEASE/reference/html5/#appendix-faq-what-is-
userdetailservice)曰く:

> `UserDetailsService` is a DAO interface for loading data that is specific to
> a user account. It has no other function other to load that data for use by
> other components within the framework. It is not responsible for
> authenticating the user.  
>  Authenticating a user with a username/password combination is most commonly
> performed by the `DaoAuthenticationProvider`, (後略)

([22.7.4. Common "Howto" Requests](https://docs.spring.io/spring-
security/site/docs/5.3.3.RELEASE/reference/html5/#appendix-faq-
howto)にある`UserDetailsService`についての疑問と回答も参照してみてください。`UserDetailsService`とは何であるか、というのがより理解できるかなと思います。)

ということで、`UserDetailsService`とはまさに「usernameの情報だけに基づいて検索」するための機構です。  
また、Spring
Securityにおいて認証処理を実装するところは`AuthenticationProvider`ですが(詳しくは[こちらの回答](https://ja.stackoverflow.com/a/67465/2808)でもリンクしている[Spring
Security Architecture](https://spring.io/guides/topicals/spring-security-
architecture/)などを参照してください)、`UserDetailsService`においては、引用文中にある通り、デフォルトでは`DaoAuthenticationProvider`が利用されるようになっています。

> なぜUserDetailsServiceを実装して、loadUserByUsernameメソッドで username
> に基づいたユーザーを返す必要があるのでしょうか？

これについては、そのようなシチュエーションに適すように設計されたものだから、としか答えようがないかと思います。  
必要がある、わけではなくてusername(ユーザのユニークキー(自然キー))でユーザ検索するのが普通だからそういう設計になっている、ので、ここにギャップがあるなら`UserDetailsService`を利用すべき状況ではないということになります。

> principal と credentials
> は2つそろうことで機能すると思うので、パスワードによる認証を行っていた場合、どのような機構で、この2つを担保しているのでしょうか？

こちらについては疑問に感じられているポイントがわかりませんでした(& princpalとcredentialsに誤解があるように思われます)。

[`UsernamePasswordAuthenticationFilter`](https://github.com/spring-
projects/spring-
security/blob/5.3.3.RELEASE/web/src/main/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java#L69-L96)と[`DaoAuthenticationProvider`](https://github.com/spring-
projects/spring-
security/blob/5.3.3.RELEASE/core/src/main/java/org/springframework/security/authentication/dao/DaoAuthenticationProvider.java#L77-L97)(及びその親クラス[`AbstractUserDetailsAuthenticationProvider`](https://github.com/spring-
projects/spring-
security/blob/5.3.3.RELEASE/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java#L126-L168))の実装を見ることで解決するでしょうか。

* * *

(以下、回答とは直接関係ない余談です)

`UserDetailsService`というのは(おそらくは多くの初学者の予想に反して)Spring Securityの応用的な使い方です。  
なので、`UserDetailsService`を用いたサンプルを以てSpring
Securityの基本を学ぼうとするのはハードルが高い(し、遠回りだ)と個人的には感じています。

できれば`UserDetailsService`を利用していないコードサンプルから始められた方が良いのではないかな、と私は考えます。

(しかし、世に溢れる初心者向けと称するコードサンプルは`UserDetailsService`を使ったものばかり…というのに自分が学び始めた頃にも感じて[Qiitaに書きなぐった](https://qiita.com/yukihane/items/865db9e7279cf0e588a7)りもしたのですが。)

追記:  
…なので自分で(私のQiitaの記事としては比較的真面目に)[書いてみました](https://qiita.com/yukihane/items/e214c0f9f4b671087caa)。  
サンプルコードは[こちらです](https://github.com/yukihane/hello-
java/tree/master/spring/springboot-auth-example-202006)。
CC BY-SA 4.0
2020-06-12T21:48:21.120
67604
2020-06-13T08:18:23.907
2020-06-13T08:18:23.907
2808
2808
67596
answer