Open rooobot opened 4 years ago
问题中函数的签名直接是三个参数,因为我平时做这个功能时,salt
值不是通过ID
来按一定的规则生成的,而是直接使用UUID
随机生成的,所以,我这里的实现函数签名有一点不太一样。
直接上代码:
密码校验工具类代码如下:
/**
* @author zhaoyang on 2020-08-25.
*/
public class PasswdCheckUtils {
private static final String PASSWD_ECD_TPL = "_(%s:%s)_";
/**
* 密码校验:
* 校验时,第一个参数传入的是前端传过来的密码,一般也不会传明文,会用一定的策略进行单向散列处理。
* 因为我个人在存储用户密码时,使用的 salt 是通过 UUID 生成的纯随机字符串,不是按某种相同的
* 规则来生成的,所以,这里将存储的用户密码和 salt 包装到 UserInfo 类中,这种方式可以最大化
* 随机性。
* @param plainPasswd 密码明文
* @param userInfo 封装了用户ID、密码和 salt
* @return boolean 校验结果
*/
public boolean checkPW(String plainPasswd, UserInfo userInfo) {
assert StringUtils.isNotBlank(plainPasswd);
assert userInfo != null;
assert StringUtils.isNotBlank(userInfo.getPasswd());
assert StringUtils.isNotBlank(userInfo.getSalt());
String encodedPasswd = encode(plainPasswd, userInfo.getSalt());
if (encodedPasswd.equals(userInfo.getPasswd())) {
return true;
}
return false;
}
private String encode(String plainPasswd, String salt) {
String passwd = plainPasswd;
int times = 3;
while (--times >= 0) {
passwd = StringUtils.md5(String.format(PASSWD_ECD_TPL, passwd, salt));
}
return passwd;
}
}
辅助用户类代码:
import java.io.Serializable;
/**
* @author zhaoyang on 2020-08-25.
*/
public class UserInfo implements Serializable {
/**
* 用户ID
*/
private String userID;
/**
* 加密后的用户密码
*/
private String passwd;
/**
* 密码加密的盐值,每个用户不同,UUID生成
*/
private String salt;
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
工具类:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author zhaoyang on 2020-08-25.
*/
public final class StringUtils {
private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static MessageDigest md5;
static {
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Initialize MD5 with MessageDigest exception", e);
}
}
public static boolean isBlank(String str) {
return str == null || "".equals(str);
}
public static boolean isNotBlank(String str) {
return !isBlank(str);
}
public static String md5(String key) {
try {
md5 = MessageDigest.getInstance("MD5");
byte[] buf = key.getBytes(StandardCharsets.UTF_8);
md5.update(buf, 0, buf.length);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
byte[] encodedValue = md5.digest();
int j = encodedValue.length;
char finalValue[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte encoded = encodedValue[i];
finalValue[k++] = hexDigits[encoded >> 4 & 0xf];
finalValue[k++] = hexDigits[encoded & 0xf];
}
return new String(finalValue);
}
}
测试用例:
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author zhaoyang on 2020-08-25.
*/
public class TestPasswdCheckUtils {
private UserInfo mockUserInfo;
@Before
public void setUp() {
mockUserInfo = new UserInfo();
mockUserInfo.setUserID("tom");
// plain password is "aaa"
mockUserInfo.setPasswd("058451f1fa61e38931ed6c5861094b75");
mockUserInfo.setSalt("bbb");
}
@Test
public void TestCheckPwTrue() {
PasswdCheckUtils checkUtils = new PasswdCheckUtils();
boolean result = checkUtils.checkPW("aaa", mockUserInfo);
assertTrue("password is correct", result);
}
@Test
public void TestCheckPwFalse() {
PasswdCheckUtils checkUtils = new PasswdCheckUtils();
boolean result = checkUtils.checkPW("aaaa", mockUserInfo);
assertFalse("password is not correct", result);
}
}
以上为全部代码。
测试用例的测试结果:
Testing started at 11:13 上午 ...
> Task :cleanTest
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
me.zy.passwdchk.TestPasswdCheckUtils > TestCheckPwTrue PASSED
me.zy.passwdchk.TestPasswdCheckUtils > TestCheckPwFalse PASSED
BUILD SUCCESSFUL in 1s
5 actionable tasks: 3 executed, 2 up-to-date
11:13:03 上午: Tasks execution finished ':cleanTest :test --tests "me.zy.passwdchk.TestPasswdCheckUtils"'.
两个测试用例全部测试通过。
问题
请用熟悉的语言编写一个密码验证函数,返回密码是否正确的
boolean
值,密码加密算法使用你认为合适的加密算法。