PasswordEncoder Delegating change
support noop pbkdf2 scrypt md4 md5 sha1 sha256 sha384 sha512 sm3 ldap
This commit is contained in:
@@ -55,9 +55,7 @@ public class DefaultJdbcAuthenticationRealm extends AbstractAuthenticationRealm
|
||||
boolean passwordMatches = false;
|
||||
_logger.info("password : "
|
||||
+ PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), password));
|
||||
passwordMatches = passwordEncoder.matches(
|
||||
PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), password),
|
||||
userInfo.getPassword());
|
||||
passwordMatches = passwordEncoder.matches(password,userInfo.getPassword());
|
||||
_logger.debug("passwordvalid : " + passwordMatches);
|
||||
if (!passwordMatches) {
|
||||
setBadPasswordCount(userInfo);
|
||||
|
||||
@@ -19,6 +19,9 @@ package org.maxkey.autoconfigure;
|
||||
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import org.maxkey.authn.RealmAuthenticationProvider;
|
||||
import org.maxkey.authn.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
@@ -29,6 +32,8 @@ import org.maxkey.authn.support.rememberme.RedisRemeberMeService;
|
||||
import org.maxkey.constants.ConstantsProperties;
|
||||
import org.maxkey.crypto.keystore.KeyStoreLoader;
|
||||
import org.maxkey.crypto.password.PasswordReciprocal;
|
||||
import org.maxkey.crypto.password.SM3PasswordEncoder;
|
||||
import org.maxkey.crypto.password.StandardPasswordEncoder;
|
||||
import org.maxkey.persistence.redis.RedisConnectionFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -45,7 +50,14 @@ import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.LdapShaPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.Md4PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
|
||||
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
||||
|
||||
|
||||
@Configuration
|
||||
@@ -120,7 +132,30 @@ public class ApplicationAutoConfiguration implements InitializingBean {
|
||||
*/
|
||||
@Bean(name = "passwordEncoder")
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
String idForEncode = "bcrypt";
|
||||
Map<String ,PasswordEncoder > encoders = new HashMap<String ,PasswordEncoder>();
|
||||
encoders.put(idForEncode, new BCryptPasswordEncoder());
|
||||
encoders.put("noop", NoOpPasswordEncoder.getInstance());
|
||||
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
|
||||
encoders.put("scrypt", new SCryptPasswordEncoder());
|
||||
//md
|
||||
encoders.put("md4", new Md4PasswordEncoder());
|
||||
encoders.put("md5", new MessageDigestPasswordEncoder("MD5"));
|
||||
//sha
|
||||
encoders.put("sha1", new StandardPasswordEncoder("SHA-1",""));
|
||||
encoders.put("sha256", new StandardPasswordEncoder());
|
||||
encoders.put("sha384", new StandardPasswordEncoder("SHA-384",""));
|
||||
encoders.put("sha512", new StandardPasswordEncoder("SHA-512",""));
|
||||
|
||||
encoders.put("sm3", new SM3PasswordEncoder());
|
||||
|
||||
encoders.put("ldap", new LdapShaPasswordEncoder());
|
||||
|
||||
//idForEncode is default for encoder
|
||||
PasswordEncoder passwordEncoder =
|
||||
new DelegatingPasswordEncoder(idForEncode, encoders);
|
||||
|
||||
return passwordEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
62
maxkey-core/src/main/java/org/maxkey/crypto/SM3.java
Normal file
62
maxkey-core/src/main/java/org/maxkey/crypto/SM3.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package org.maxkey.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
/**
|
||||
* SM3.
|
||||
* @author Crystal.Sea
|
||||
*
|
||||
*/
|
||||
public class SM3 {
|
||||
/**
|
||||
* 计算SM3摘要值
|
||||
*
|
||||
* @param simple 原文
|
||||
* @return 摘要值,对于SM3算法来说是32字节
|
||||
*/
|
||||
public static byte[] encode(byte[] simple) {
|
||||
SM3Digest digest = new SM3Digest();
|
||||
digest.update(simple, 0, simple.length);
|
||||
byte[] hash = new byte[digest.getDigestSize()];
|
||||
digest.doFinal(hash, 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证摘要
|
||||
*
|
||||
* @param simple 原文
|
||||
* @param cipher 摘要值
|
||||
* @return 返回true标识验证成功,false标识验证失败
|
||||
*/
|
||||
public static boolean verify(byte[] simple, byte[] cipher) {
|
||||
byte[] newHash = encode(simple);
|
||||
if (Arrays.equals(newHash, cipher)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算SM3 Mac值
|
||||
*
|
||||
* @param key key值,可以是任意长度的字节数组
|
||||
* @param srcData 原文
|
||||
* @return Mac值,对于HMac-SM3来说是32字节
|
||||
*/
|
||||
public static byte[] hmac(byte[] key, byte[] simple) {
|
||||
KeyParameter keyParameter = new KeyParameter(key);
|
||||
SM3Digest digest = new SM3Digest();
|
||||
HMac mac = new HMac(digest);
|
||||
mac.init(keyParameter);
|
||||
mac.update(simple, 0, simple.length);
|
||||
byte[] result = new byte[mac.getMacSize()];
|
||||
mac.doFinal(result, 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.maxkey.crypto.password;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class Digester {
|
||||
|
||||
private final String algorithm;
|
||||
|
||||
private int iterations;
|
||||
|
||||
/**
|
||||
* Create a new Digester.
|
||||
* @param algorithm the digest algorithm; for example, "SHA-1" or "SHA-256".
|
||||
* @param iterations the number of times to apply the digest algorithm to the input
|
||||
*/
|
||||
Digester(String algorithm, int iterations) {
|
||||
// eagerly validate the algorithm
|
||||
createDigest(algorithm);
|
||||
this.algorithm = algorithm;
|
||||
setIterations(iterations);
|
||||
}
|
||||
|
||||
public byte[] digest(byte[] value) {
|
||||
MessageDigest messageDigest = createDigest(algorithm);
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
value = messageDigest.digest(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void setIterations(int iterations) {
|
||||
if (iterations <= 0) {
|
||||
throw new IllegalArgumentException("Iterations value must be greater than zero");
|
||||
}
|
||||
this.iterations = iterations;
|
||||
}
|
||||
|
||||
private static MessageDigest createDigest(String algorithm) {
|
||||
try {
|
||||
return MessageDigest.getInstance(algorithm);
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("No such hashing algorithm", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.maxkey.crypto.password;
|
||||
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.maxkey.crypto.*;
|
||||
|
||||
public class SM3PasswordEncoder implements PasswordEncoder {
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
String cipher = new String(Hex.encode(SM3.encode(rawPassword.toString().getBytes())));
|
||||
return cipher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
String cipher = encode(rawPassword);
|
||||
return encodedPassword.equals(cipher);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.maxkey.crypto.password;
|
||||
|
||||
import static org.springframework.security.crypto.util.EncodingUtils.concatenate;
|
||||
import static org.springframework.security.crypto.util.EncodingUtils.subArray;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
public final class StandardPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
private final Digester digester;
|
||||
|
||||
private final byte[] secret;
|
||||
|
||||
private final BytesKeyGenerator saltGenerator;
|
||||
|
||||
/**
|
||||
* Constructs a standard password encoder with no additional secret value.
|
||||
*/
|
||||
public StandardPasswordEncoder() {
|
||||
this("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a standard password encoder with a secret value which is also included
|
||||
* in the password hash.
|
||||
*
|
||||
* @param secret the secret key used in the encoding process (should not be shared)
|
||||
*/
|
||||
public StandardPasswordEncoder(CharSequence secret) {
|
||||
this("SHA-256", secret);
|
||||
}
|
||||
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return encode(rawPassword, saltGenerator.generateKey());
|
||||
}
|
||||
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
byte[] digested = decode(encodedPassword);
|
||||
byte[] salt = subArray(digested, 0, saltGenerator.getKeyLength());
|
||||
return MessageDigest.isEqual(digested, digest(rawPassword, salt));
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
public StandardPasswordEncoder(String algorithm, CharSequence secret) {
|
||||
this.digester = new Digester(algorithm, DEFAULT_ITERATIONS);
|
||||
this.secret = Utf8.encode(secret);
|
||||
this.saltGenerator = KeyGenerators.secureRandom();
|
||||
}
|
||||
|
||||
private String encode(CharSequence rawPassword, byte[] salt) {
|
||||
byte[] digest = digest(rawPassword, salt);
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
|
||||
private byte[] digest(CharSequence rawPassword, byte[] salt) {
|
||||
byte[] digest = digester.digest(concatenate(salt, secret,
|
||||
Utf8.encode(rawPassword)));
|
||||
return concatenate(salt, digest);
|
||||
}
|
||||
|
||||
private byte[] decode(CharSequence encodedPassword) {
|
||||
return Hex.decode(encodedPassword);
|
||||
}
|
||||
|
||||
private static final int DEFAULT_ITERATIONS = 1024;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.maxkey.crypto.password;
|
||||
|
||||
public class SM3PasswordEncoderTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// TODO Auto-generated method stub
|
||||
SM3PasswordEncoder sm3 = new SM3PasswordEncoder();
|
||||
System.out.println(sm3.encode("maxkeypassword"));
|
||||
|
||||
String c="f4679d46e96d95d67db4c8c91fcf8aaaa4e1d437ffee278d2ea97f41f7f48c12";
|
||||
System.out.println(sm3.matches("maxkeypassword",c));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -143,7 +143,7 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
|
||||
public UserInfo passwordEncoder(UserInfo userInfo) {
|
||||
//密码不为空,则需要进行加密处理
|
||||
if(userInfo.getPassword()!=null && !userInfo.getPassword().equals("")) {
|
||||
String password = passwordEncoder.encode(PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), userInfo.getPassword()));
|
||||
String password = passwordEncoder.encode(userInfo.getPassword());
|
||||
userInfo.setDecipherable(ReciprocalUtils.encode(PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), userInfo.getPassword())));
|
||||
_logger.debug("decipherable : "+userInfo.getDecipherable());
|
||||
userInfo.setPassword(password);
|
||||
|
||||
Reference in New Issue
Block a user