67492


[spring-security/SecurityConfig.java at
1e33627d87de0949c3f281ac8d3f4476e44d0093 · spring-projects/spring-
security](https://github.com/spring-projects/spring-
security/blob/1e33627d87de0949c3f281ac8d3f4476e44d0093/samples/boot/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java)

上記公式のサンプルに

[code]

    UserDetails userDetails = User.withDefaultPasswordEncoder()
        .username("user")
        .password("password")
        .roles("USER")
        .build();
    return new InMemoryUserDetailsManager(userDetails);
    
[/code]

という記載があります。おなじように同リポジトリには `withDefaultPasswordEncoder`
を使った例が他にもたくさんあり、なるほど、これを使うのかと思いよくよく `withDefaultPasswordEncoder` の定義を見に行くと....

[spring-security/User.java at master · spring-projects/spring-
security](https://github.com/spring-projects/spring-
security/blob/master/core/src/main/java/org/springframework/security/core/userdetails/User.java#L332)

[code]

     * @deprecated Using this method is not considered safe for production, but is
     * acceptable for demos and getting started. For production purposes, ensure the
     * password is encoded externally. See the method Javadoc for additional details.
     * There are no plans to remove this support. It is deprecated to indicate
     * that this is considered insecure for production purposes.
     */
    @Deprecated
    public static UserBuilder withDefaultPasswordEncoder() {
    
[/code]

**本番では使用しないでください。デモとかで使うものです。メソッド自体は消すつもりはありませんが、Deprecatedとのこと！！**

ということで、下記参考リンクのようにするのが一般的なのでしょうか？  
[Spring Securityでユーザ認証を実装してみる -
ももいろテクノロジー](http://inaz2.hatenablog.com/entry/2017/04/18/222721)

> registerAdmin/registerUserメソッドでは、DBに保存する前にパスワードをBCryptでハッシュ化する。
> passwordEncoderメンバは、@AutoWiredによるDependency
> Injectionにより、実行時にWebSecurityConfigクラスのメソッドが呼ばれて代入される。

つまり `WebSecurityConfigurerAdapter`
を継承したクラスで暗号化方法を設定するので、その方法をDIでもらって新規作成の処理で使うというやりかたが一般的な形なのでしょうか？


CC BY-SA 4.0
2020-06-10T01:56:28.057
67477
2020-06-10T14:48:29.127
2020-06-10T02:26:15.647
9008
9008
question
java spring spring-boot spring-security
Spring Securityでユーザー登録時のパスワードの暗号化の一般的な書き方はありますか？


(`User`クラス自体、デモ以外で使うことはあまり無いのでは…という思いは置いておきつつ、)

Spring Securityのドキュメントを当メソッド名で検索すると注意文章がいくつかヒットしますが、例えば、[10.10.4. In-Memory
Authentication](https://docs.spring.io/spring-
security/site/docs/5.3.3.RELEASE/reference/html5/#servlet-authentication-
inmemory)

> In the sample below we leverage User.withDefaultPasswordEncoder to ensure
> that the password stored in memory is protected. However, it does not
> protect the password against obtaining the password by decompiling the
> source code.

とあるように、メソッド自身に問題がある、というよりは、パスワードをソースコードに直書きしがちなメソッドを提供してしまっていることが問題だ(本来パスワードをプロパティとして持たせたいなら[外部でエンコードしたものを取り扱うべきだ](https://docs.spring.io/spring-
security/site/docs/5.3.3.RELEASE/reference/html5/#authentication-password-
storage-dep-getting-started))ということのように読めます。

* * *

> `WebSecurityConfigurerAdapter`
> を継承したクラスで暗号化方法を設定するので、その方法をDIでもらって新規作成の処理で使うというやりかたが一般的な形なのでしょうか？

Bean化すること自体はSpring的には特に変ではないかなと思います。他方、必要な箇所で`new
BCryptPasswordEncoder()`していたとしてもこれも別に変ではないと思います。

ただ、今回のコードは個人的には微妙なやり方(デモとしてなら許されるが…)だと思います。  
今回のコードでは、ユーザ情報登録時のパスワードエンコード処理に使うもの

[code]

    @Autowired
    private PasswordEncoder passwordEncoder;
    ...
        User user = new User(username, passwordEncoder.encode(password), mailAddress);
    
[/code]

と、認証処理時に使うもの

[code]

    auth
        .userDetailsService(userService)
        .passwordEncoder(passwordEncoder());
    
[/code]

の2つ(だけ)が`BCryptPasswordEncoder`であれば良いですが、

[code]

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
[/code]

としてしまうと、それ以外の場所でも`PasswordEncoder`の実装として`BCryptPasswordEncoder`がインジェクションされるようになってしまいます。  
意図的にそうするのであれば良いですが、一般的には、スコープが広すぎると思います。  
(今回のデモではこの2つで全部なので問題ないが…ということです)

また、仮に、`PasswordEncoder`の実装として全ての処理で`BCryptPasswordEncoder`を用いるのだ、という場合には、確かにそこに書かれているようにBean定義することになりますが、その場合

[code]

    auth
        .userDetailsService(userService)
        .passwordEncoder(passwordEncoder());
    
[/code]

で行っている`.passwordEncoder(passwordEncoder())`は不要です。  
(デフォルトを`BCryptPasswordEncoder`と決めたのだから明示的に設定せずともそれが用いられます。)

[Password Storage Configuration - 5.1.2. Password
Storage](https://docs.spring.io/spring-
security/site/docs/5.3.3.RELEASE/reference/html5/#authentication-password-
storage-configuration):

> Spring Security uses DelegatingPasswordEncoder by default. However, this can
> be customized by exposing a PasswordEncoder as a Spring bean.

[上記文章に対応する実装](https://github.com/spring-projects/spring-
security/blob/5.3.3.RELEASE/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java#L616-L619)

* * *

現実的には、`UserDetailsService`の認証ではbcryptを使うと決めたが他はそうであるとは限らないのでデフォルト設定のままにしておきたい、みたいなこともあるかと思います。  
その場合、Beanにするにしても[`@Qualifier`](https://www.baeldung.com/spring-qualifier-
annotation)を使用してスコープを狭めた方が良いと考えます。

[code]

    @Configuration
    public class PasswordEncoderConfiguration {
    
        @Bean
        @Primary
        public PasswordEncoder passwordEncoder() {
            return PasswordEncoderFactories.createDelegatingPasswordEncoder();
        }
    
        // 今回の2箇所ではこちらを用いる: @Qualifier("userDetailsServicePasswordEncoder")
        @Bean
        public PasswordEncoder userDetailsServicePasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    
[/code]

(…というふうに実装することを考えると、デフォルトとして採用しないのであれば、`PasswordEncoder`自身をBean化するメリットはあまり無さそうかな、という気もしてきました)

追記:  
`@Qualifier`を用いるのではなく、`BCryptPasswordEncoder`は`BCryptPasswordEncoder`型のBeanとして定義する(ことで`PasswordEncoder`としてはインジェクションさせない)ようにしているものをいくつか見かけました([例](https://auth0.com/blog/implementing-
jwt-authentication-on-spring-boot/))。


CC BY-SA 4.0
2020-06-10T06:10:49.607
67492
2020-06-10T14:48:29.127
2020-06-10T14:48:29.127
2808
2808
67477
answer