123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571 |
- [[authentication-password-storage]]
- = Password Storage
- Spring Security's `PasswordEncoder` interface is used to perform a one-way transformation of a password to let the password be stored securely.
- Given `PasswordEncoder` is a one-way transformation, it is not useful when the password transformation needs to be two-way (such as storing credentials used to authenticate to a database).
- Typically, `PasswordEncoder` is used for storing a password that needs to be compared to a user-provided password at the time of authentication.
- [[authentication-password-storage-history]]
- == Password Storage History
- Throughout the years, the standard mechanism for storing passwords has evolved.
- In the beginning, passwords were stored in plaintext.
- The passwords were assumed to be safe because the data store the passwords were saved in required credentials to access it.
- However, malicious users were able to find ways to get large "`data dumps`" of usernames and passwords by using attacks such as SQL Injection.
- As more and more user credentials became public, security experts realized that we needed to do more to protect users' passwords.
- Developers were then encouraged to store passwords after running them through a one way hash, such as SHA-256.
- When a user tried to authenticate, the hashed password would be compared to the hash of the password that they typed.
- This meant that the system only needed to store the one-way hash of the password.
- If a breach occurred, only the one-way hashes of the passwords were exposed.
- Since the hashes were one-way and it was computationally difficult to guess the passwords given the hash, it would not be worth the effort to figure out each password in the system.
- To defeat this new system, malicious users decided to create lookup tables known as https://en.wikipedia.org/wiki/Rainbow_table[Rainbow Tables].
- Rather than doing the work of guessing each password every time, they computed the password once and stored it in a lookup table.
- To mitigate the effectiveness of Rainbow Tables, developers were encouraged to use salted passwords.
- Instead of using just the password as input to the hash function, random bytes (known as salt) would be generated for every user's password.
- The salt and the user's password would be run through the hash function to produce a unique hash.
- The salt would be stored alongside the user's password in clear text.
- Then when a user tried to authenticate, the hashed password would be compared to the hash of the stored salt and the password that they typed.
- The unique salt meant that Rainbow Tables were no longer effective because the hash was different for every salt and password combination.
- In modern times, we realize that cryptographic hashes (like SHA-256) are no longer secure.
- The reason is that with modern hardware we can perform billions of hash calculations a second.
- This means that we can crack each password individually with ease.
- Developers are now encouraged to leverage adaptive one-way functions to store a password.
- Validation of passwords with adaptive one-way functions are intentionally resource-intensive (they intentionally use a lot of CPU, memory, or other resources).
- An adaptive one-way function allows configuring a "`work factor`" that can grow as hardware gets better.
- We recommend that the "`work factor`" be tuned to take about one second to verify a password on your system.
- This trade off is to make it difficult for attackers to crack the password, but not so costly that it puts excessive burden on your own system or irritates users.
- Spring Security has attempted to provide a good starting point for the "`work factor`", but we encourage users to customize the "`work factor`" for their own system, since the performance varies drastically from system to system.
- Examples of adaptive one-way functions that should be used include <<authentication-password-storage-bcrypt,bcrypt>>, <<authentication-password-storage-pbkdf2,PBKDF2>>, <<authentication-password-storage-scrypt,scrypt>>, and <<authentication-password-storage-argon2,argon2>>.
- Because adaptive one-way functions are intentionally resource intensive, validating a username and password for every request can significantly degrade the performance of an application.
- There is nothing Spring Security (or any other library) can do to speed up the validation of the password, since security is gained by making the validation resource intensive.
- Users are encouraged to exchange the long term credentials (that is, username and password) for a short term credential (such as a session, and OAuth Token, and so on).
- The short term credential can be validated quickly without any loss in security.
- [[authentication-password-storage-dpe]]
- == DelegatingPasswordEncoder
- Prior to Spring Security 5.0, the default `PasswordEncoder` was `NoOpPasswordEncoder`, which required plain-text passwords.
- Based on the <<authentication-password-storage-history,Password History>> section, you might expect that the default `PasswordEncoder` would now be something like `BCryptPasswordEncoder`.
- However, this ignores three real world problems:
- - Many applications use old password encodings that cannot easily migrate.
- - The best practice for password storage will change again.
- - As a framework, Spring Security cannot make breaking changes frequently.
- Instead Spring Security introduces `DelegatingPasswordEncoder`, which solves all of the problems by:
- - Ensuring that passwords are encoded by using the current password storage recommendations
- - Allowing for validating passwords in modern and legacy formats
- - Allowing for upgrading the encoding in the future
- You can easily construct an instance of `DelegatingPasswordEncoder` by using `PasswordEncoderFactories`:
- .Create Default DelegatingPasswordEncoder
- ====
- .Java
- [source,java,role="primary"]
- ----
- PasswordEncoder passwordEncoder =
- PasswordEncoderFactories.createDelegatingPasswordEncoder();
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- val passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
- ----
- ====
- Alternatively, you can create your own custom instance:
- .Create Custom DelegatingPasswordEncoder
- ====
- .Java
- [source,java,role="primary"]
- ----
- String idForEncode = "bcrypt";
- Map encoders = new HashMap<>();
- encoders.put(idForEncode, new BCryptPasswordEncoder());
- encoders.put("noop", NoOpPasswordEncoder.getInstance());
- encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
- encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
- encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
- encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
- encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
- encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
- encoders.put("sha256", new StandardPasswordEncoder());
- PasswordEncoder passwordEncoder =
- new DelegatingPasswordEncoder(idForEncode, encoders);
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- val idForEncode = "bcrypt"
- val encoders: MutableMap<String, PasswordEncoder> = mutableMapOf()
- encoders[idForEncode] = BCryptPasswordEncoder()
- encoders["noop"] = NoOpPasswordEncoder.getInstance()
- encoders["pbkdf2"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()
- encoders["pbkdf2@SpringSecurity_v5_8"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
- encoders["scrypt"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()
- encoders["scrypt@SpringSecurity_v5_8"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
- encoders["argon2"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()
- encoders["argon2@SpringSecurity_v5_8"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
- encoders["sha256"] = StandardPasswordEncoder()
- val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, encoders)
- ----
- ====
- [[authentication-password-storage-dpe-format]]
- === Password Storage Format
- The general format for a password is:
- .DelegatingPasswordEncoder Storage Format
- ====
- [source,text,attrs="-attributes"]
- ----
- {id}encodedPassword
- ----
- ====
- `id` is an identifier that is used to look up which `PasswordEncoder` should be used and `encodedPassword` is the original encoded password for the selected `PasswordEncoder`.
- The `id` must be at the beginning of the password, start with `{`, and end with `}`.
- If the `id` cannot be found, the `id` is set to null.
- For example, the following might be a list of passwords encoded using different `id` values.
- All of the original passwords are `password`.
- .DelegatingPasswordEncoder Encoded Passwords Example
- ====
- [source,text,attrs="-attributes"]
- ----
- {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG // <1>
- {noop}password // <2>
- {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc // <3>
- {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc= // <4>
- {sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0 // <5>
- ----
- ====
- <1> The first password has a `PasswordEncoder` id of `bcrypt` and an `encodedPassword` value of `$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG`.
- When matching, it would delegate to `BCryptPasswordEncoder`
- <2> The second password has a `PasswordEncoder` id of `noop` and `encodedPassword` value of `password`.
- When matching, it would delegate to `NoOpPasswordEncoder`
- <3> The third password has a `PasswordEncoder` id of `pbkdf2` and `encodedPassword` value of `5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc`.
- When matching, it would delegate to `Pbkdf2PasswordEncoder`
- <4> The fourth password has a `PasswordEncoder` id of `scrypt` and `encodedPassword` value of `$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=`
- When matching, it would delegate to `SCryptPasswordEncoder`
- <5> The final password has a `PasswordEncoder` id of `sha256` and `encodedPassword` value of `97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0`.
- When matching, it would delegate to `StandardPasswordEncoder`
- [NOTE]
- ====
- Some users might be concerned that the storage format is provided for a potential hacker.
- This is not a concern because the storage of the password does not rely on the algorithm being a secret.
- Additionally, most formats are easy for an attacker to figure out without the prefix.
- For example, BCrypt passwords often start with `$2a$`.
- ====
- [[authentication-password-storage-dpe-encoding]]
- === Password Encoding
- The `idForEncode` passed into the constructor determines which `PasswordEncoder` is used for encoding passwords.
- In the `DelegatingPasswordEncoder` we constructed earlier, that means that the result of encoding `password` is delegated to `BCryptPasswordEncoder` and be prefixed with `+{bcrypt}+`.
- The end result looks like the following example:
- .DelegatingPasswordEncoder Encode Example
- ====
- [source,text,attrs="-attributes"]
- ----
- {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
- ----
- ====
- [[authentication-password-storage-dpe-matching]]
- === Password Matching
- Matching is based upon the `+{id}+` and the mapping of the `id` to the `PasswordEncoder` provided in the constructor.
- Our example in <<authentication-password-storage-dpe-format,Password Storage Format>> provides a working example of how this is done.
- By default, the result of invoking `matches(CharSequence, String)` with a password and an `id` that is not mapped (including a null id) results in an `IllegalArgumentException`.
- This behavior can be customized by using `DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(PasswordEncoder)`.
- By using the `id`, we can match on any password encoding but encode passwords by using the most modern password encoding.
- This is important, because unlike encryption, password hashes are designed so that there is no simple way to recover the plaintext.
- Since there is no way to recover the plaintext, it is difficult to migrate the passwords.
- While it is simple for users to migrate `NoOpPasswordEncoder`, we chose to include it by default to make it simple for the getting-started experience.
- [[authentication-password-storage-dep-getting-started]]
- === Getting Started Experience
- If you are putting together a demo or a sample, it is a bit cumbersome to take time to hash the passwords of your users.
- There are convenience mechanisms to make this easier, but this is still not intended for production.
- .withDefaultPasswordEncoder Example
- ====
- .Java
- [source,java,role="primary",attrs="-attributes"]
- ----
- User user = User.withDefaultPasswordEncoder()
- .username("user")
- .password("password")
- .roles("user")
- .build();
- System.out.println(user.getPassword());
- // {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
- ----
- .Kotlin
- [source,kotlin,role="secondary",attrs="-attributes"]
- ----
- val user = User.withDefaultPasswordEncoder()
- .username("user")
- .password("password")
- .roles("user")
- .build()
- println(user.password)
- // {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
- ----
- ====
- If you are creating multiple users, you can also reuse the builder:
- .withDefaultPasswordEncoder Reusing the Builder
- ====
- .Java
- [source,java,role="primary"]
- ----
- UserBuilder users = User.withDefaultPasswordEncoder();
- User user = users
- .username("user")
- .password("password")
- .roles("USER")
- .build();
- User admin = users
- .username("admin")
- .password("password")
- .roles("USER","ADMIN")
- .build();
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- val users = User.withDefaultPasswordEncoder()
- val user = users
- .username("user")
- .password("password")
- .roles("USER")
- .build()
- val admin = users
- .username("admin")
- .password("password")
- .roles("USER", "ADMIN")
- .build()
- ----
- ====
- This does hash the password that is stored, but the passwords are still exposed in memory and in the compiled source code.
- Therefore, it is still not considered secure for a production environment.
- For production, you should <<authentication-password-storage-boot-cli,hash your passwords externally>>.
- [[authentication-password-storage-boot-cli]]
- === Encode with Spring Boot CLI
- The easiest way to properly encode your password is to use the https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-cli.html[Spring Boot CLI].
- For example, the following example encodes the password of `password` for use with <<authentication-password-storage-dpe>>:
- .Spring Boot CLI encodepassword Example
- ====
- [source,attrs="-attributes"]
- ----
- spring encodepassword password
- {bcrypt}$2a$10$X5wFBtLrL/kHcmrOGGTrGufsBX8CJ0WpQpF3pgeuxBB/H73BK1DW6
- ----
- ====
- [[authentication-password-storage-dpe-troubleshoot]]
- === Troubleshooting
- The following error occurs when one of the passwords that are stored has no `id`, as described in <<authentication-password-storage-dpe-format>>.
- ====
- ----
- java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
- at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:233)
- at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:196)
- ----
- ====
- The easiest way to resolve it is to figure out how your passwords are currently being stored and explicitly provide the correct `PasswordEncoder`.
- If you are migrating from Spring Security 4.2.x, you can revert to the previous behavior by <<authentication-password-storage-configuration,exposing a `NoOpPasswordEncoder` bean>>.
- Alternatively, you can prefix all of your passwords with the correct `id` and continue to use `DelegatingPasswordEncoder`.
- For example, if you are using BCrypt, you would migrate your password from something like:
- ====
- ----
- $2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
- ----
- ====
- to
- ====
- [source,attrs="-attributes"]
- ----
- {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
- ----
- ====
- For a complete listing of the mappings, see the Javadoc for
- https://docs.spring.io/spring-security/site/docs/5.0.x/api/org/springframework/security/crypto/factory/PasswordEncoderFactories.html[`PasswordEncoderFactories`].
- [[authentication-password-storage-bcrypt]]
- == BCryptPasswordEncoder
- The `BCryptPasswordEncoder` implementation uses the widely supported https://en.wikipedia.org/wiki/Bcrypt[bcrypt] algorithm to hash the passwords.
- To make it more resistant to password cracking, bcrypt is deliberately slow.
- Like other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.
- The default implementation of `BCryptPasswordEncoder` uses strength 10 as mentioned in the Javadoc of https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.html[`BCryptPasswordEncoder`]. You are encouraged to
- tune and test the strength parameter on your own system so that it takes roughly 1 second to verify a password.
- .BCryptPasswordEncoder
- ====
- .Java
- [source,java,role="primary"]
- ----
- // Create an encoder with strength 16
- BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
- String result = encoder.encode("myPassword");
- assertTrue(encoder.matches("myPassword", result));
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- // Create an encoder with strength 16
- val encoder = BCryptPasswordEncoder(16)
- val result: String = encoder.encode("myPassword")
- assertTrue(encoder.matches("myPassword", result))
- ----
- ====
- [[authentication-password-storage-argon2]]
- == Argon2PasswordEncoder
- The `Argon2PasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/Argon2[Argon2] algorithm to hash the passwords.
- Argon2 is the winner of the https://en.wikipedia.org/wiki/Password_Hashing_Competition[Password Hashing Competition].
- To defeat password cracking on custom hardware, Argon2 is a deliberately slow algorithm that requires large amounts of memory.
- Like other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.
- The current implementation of the `Argon2PasswordEncoder` requires BouncyCastle.
- .Argon2PasswordEncoder
- ====
- .Java
- [source,java,role="primary"]
- ----
- // Create an encoder with all the defaults
- Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
- String result = encoder.encode("myPassword");
- assertTrue(encoder.matches("myPassword", result));
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- // Create an encoder with all the defaults
- val encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
- val result: String = encoder.encode("myPassword")
- assertTrue(encoder.matches("myPassword", result))
- ----
- ====
- [[authentication-password-storage-pbkdf2]]
- == Pbkdf2PasswordEncoder
- The `Pbkdf2PasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/PBKDF2[PBKDF2] algorithm to hash the passwords.
- To defeat password cracking PBKDF2 is a deliberately slow algorithm.
- Like other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.
- This algorithm is a good choice when FIPS certification is required.
- .Pbkdf2PasswordEncoder
- ====
- .Java
- [source,java,role="primary"]
- ----
- // Create an encoder with all the defaults
- Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
- String result = encoder.encode("myPassword");
- assertTrue(encoder.matches("myPassword", result));
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- // Create an encoder with all the defaults
- val encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
- val result: String = encoder.encode("myPassword")
- assertTrue(encoder.matches("myPassword", result))
- ----
- ====
- [[authentication-password-storage-scrypt]]
- == SCryptPasswordEncoder
- The `SCryptPasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/Scrypt[scrypt] algorithm to hash the passwords.
- To defeat password cracking on custom hardware, scrypt is a deliberately slow algorithm that requires large amounts of memory.
- Like other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.
- .SCryptPasswordEncoder
- ====
- .Java
- [source,java,role="primary"]
- ----
- // Create an encoder with all the defaults
- SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
- String result = encoder.encode("myPassword");
- assertTrue(encoder.matches("myPassword", result));
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- // Create an encoder with all the defaults
- val encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
- val result: String = encoder.encode("myPassword")
- assertTrue(encoder.matches("myPassword", result))
- ----
- ====
- [[authentication-password-storage-other]]
- == Other ``PasswordEncoder``s
- There are a significant number of other `PasswordEncoder` implementations that exist entirely for backward compatibility.
- They are all deprecated to indicate that they are no longer considered secure.
- However, there are no plans to remove them, since it is difficult to migrate existing legacy systems.
- [[authentication-password-storage-configuration]]
- == Password Storage Configuration
- Spring Security uses <<authentication-password-storage-dpe>> by default.
- However, you can customize this by exposing a `PasswordEncoder` as a Spring bean.
- If you are migrating from Spring Security 4.2.x, you can revert to the previous behavior by exposing a `NoOpPasswordEncoder` bean.
- [WARNING]
- ====
- Reverting to `NoOpPasswordEncoder` is not considered to be secure.
- You should instead migrate to using `DelegatingPasswordEncoder` to support secure password encoding.
- ====
- .NoOpPasswordEncoder
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Bean
- public static NoOpPasswordEncoder passwordEncoder() {
- return NoOpPasswordEncoder.getInstance();
- }
- ----
- .XML
- [source,xml,role="secondary"]
- ----
- <b:bean id="passwordEncoder"
- class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/>
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @Bean
- fun passwordEncoder(): PasswordEncoder {
- return NoOpPasswordEncoder.getInstance();
- }
- ----
- ====
- [NOTE]
- ====
- XML Configuration requires the `NoOpPasswordEncoder` bean name to be `passwordEncoder`.
- ====
- [[authentication-change-password-configuration]]
- == Change Password Configuration
- Most applications that allow a user to specify a password also require a feature for updating that password.
- https://w3c.github.io/webappsec-change-password-url/[A Well-Known URL for Changing Passwords] indicates a mechanism by which password managers can discover the password update endpoint for a given application.
- You can configure Spring Security to provide this discovery endpoint.
- For example, if the change password endpoint in your application is `/change-password`, then you can configure Spring Security like so:
- .Default Change Password Endpoint
- ====
- .Java
- [source,java,role="primary"]
- ----
- http
- .passwordManagement(Customizer.withDefaults())
- ----
- .XML
- [source,xml,role="secondary"]
- ----
- <sec:password-management/>
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- http {
- passwordManagement { }
- }
- ----
- ====
- Then, when a password manager navigates to `/.well-known/change-password` then Spring Security will redirect your endpoint, `/change-password`.
- Or, if your endpoint is something other than `/change-password`, you can also specify that like so:
- .Change Password Endpoint
- ====
- .Java
- [source,java,role="primary"]
- ----
- http
- .passwordManagement((management) -> management
- .changePasswordPage("/update-password")
- )
- ----
- .XML
- [source,xml,role="secondary"]
- ----
- <sec:password-management change-password-page="/update-password"/>
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- http {
- passwordManagement {
- changePasswordPage = "/update-password"
- }
- }
- ----
- ====
- With the above configuration, when a password manager navigates to `/.well-known/change-password`, then Spring Security will redirect to `/update-password`.
|