Skip to content

Commit d152a2e

Browse files
committed
Add PasswordEncoderFactories
Issue gh-4666
1 parent d0332eb commit d152a2e

File tree

3 files changed

+142
-1
lines changed

3 files changed

+142
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2002-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.crypto.factory;
18+
19+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
20+
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
21+
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
22+
import org.springframework.security.crypto.password.PasswordEncoder;
23+
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
24+
import org.springframework.security.crypto.password.StandardPasswordEncoder;
25+
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
26+
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
30+
/**
31+
* Used for creating {@link PasswordEncoder} instances
32+
* @author Rob Winch
33+
* @since 5.0
34+
*/
35+
public class PasswordEncoderFactories {
36+
37+
/**
38+
* Creates a {@link DelegatingPasswordEncoder} with default mappings. Additional
39+
* mappings may be added and the encoding will be updated to conform with best
40+
* practices. However, due to the nature of {@link DelegatingPasswordEncoder} the
41+
* updates should not impact users. The mappings current are:
42+
*
43+
* <ul>
44+
* <li>noop - {@link NoOpPasswordEncoder}</li>
45+
* <li>pbkdf2 - {@link Pbkdf2PasswordEncoder} (Also used for encoding)</li>
46+
* <li>scrypt - {@link SCryptPasswordEncoder}</li>
47+
* <li>sha256 - {@link StandardPasswordEncoder}</li>
48+
* </ul>
49+
*
50+
* @return the {@link PasswordEncoder} to use
51+
*/
52+
public static PasswordEncoder createDelegatingPasswordEncoder() {
53+
String encodingId = "bcrypt";
54+
Map<String,PasswordEncoder> encoders = new HashMap<>();
55+
encoders.put(encodingId, new BCryptPasswordEncoder());
56+
encoders.put("noop", NoOpPasswordEncoder.getInstance());
57+
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
58+
encoders.put("scrypt", new SCryptPasswordEncoder());
59+
encoders.put("sha256", new StandardPasswordEncoder());
60+
61+
return new DelegatingPasswordEncoder(encodingId, encoders);
62+
}
63+
64+
private PasswordEncoderFactories() {}
65+
}

crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
*
2828
* <h2>Constructing an instance</h2>
2929
*
30+
* You can easily construct an instance using
31+
* {@link org.springframework.security.crypto.factory.PasswordEncoderFactories}.
32+
* Alternatively, you may create your own custom instance. For example:
3033
*
3134
* <pre>
3235
* String idForEncode = "bcrypt";
@@ -73,7 +76,7 @@
7376
* When matching it would delegate to
7477
* {@link org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder}</li>
7578
* <li>The second password would have a {@code PasswordEncoder} id of "noop" and
76-
* encodedPassword of "thisisextremelyunsafe". When matching it would delegate to
79+
* encodedPassword of "password". When matching it would delegate to
7780
* {@link NoOpPasswordEncoder}</li>
7881
* <li>The third password would have a {@code PasswordEncoder} id of "pbkdf2" and
7982
* encodedPassword of
@@ -113,6 +116,8 @@
113116
* {@link IllegalArgumentException}. This behavior can be customized using
114117
* {@link #setDefaultPasswordEncoderForMatches(PasswordEncoder)}.
115118
*
119+
* @see org.springframework.security.crypto.factory.PasswordEncoderFactories
120+
*
116121
* @author Rob Winch
117122
* @since 5.0
118123
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2002-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.crypto.factory;
18+
19+
import org.junit.Test;
20+
import org.springframework.security.crypto.password.PasswordEncoder;
21+
22+
import static org.assertj.core.api.Assertions.*;
23+
24+
/**
25+
* @author Rob Winch
26+
* @since 5.0
27+
*/
28+
public class PasswordEncoderFactoriesTests {
29+
private PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
30+
31+
private String rawPassword = "password";
32+
33+
@Test
34+
public void encodeWhenDefaultThenBCryptUsed() {
35+
String encodedPassword = this.encoder.encode(this.rawPassword);
36+
37+
assertThat(encodedPassword).startsWith("{bcrypt}");
38+
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
39+
}
40+
41+
@Test
42+
public void matchesWhenBCryptThenWorks() {
43+
String encodedPassword = "{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG";
44+
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
45+
}
46+
47+
@Test
48+
public void matchesWhenNoopThenWorks() {
49+
String encodedPassword = "{noop}password";
50+
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
51+
}
52+
53+
@Test
54+
public void matchesWhenPbkdf2ThenWorks() {
55+
String encodedPassword = "{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc";
56+
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
57+
}
58+
59+
@Test
60+
public void matchesWhenSCryptThenWorks() {
61+
String encodedPassword = "{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=";
62+
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
63+
}
64+
65+
@Test
66+
public void matchesWhenSha256ThenWorks() {
67+
String encodedPassword = "{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0";
68+
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
69+
}
70+
71+
}

0 commit comments

Comments
 (0)