密码强度校验(密码不能包含账号名及相似字符数字如2-z,1-l,0-o)
记一次坑爹现场提出的刁钻问题:
对密码强度进行校验,应符合以下条件:
1.口令长度应至少10位;
2.口令应包括数字、小写字母、大写字母、特殊符号4类中至少3类;
3.口令应与用户名无相关性,口令中不得包含用户名的完整字符串、大小写变位或形似变换的字符串;
4.应更换系统或设备的出厂默认口令;
5.口令设置应避免键盘排序密码。
首先口令长度,数字,大小写字母,特殊符号就不用说了 ,第四点可以通过系统功能(如首次登陆修改密码)等功能实现,相信一个差不多的项目都会有这个功能,1.2.4百度上有很多合适的解决方案,我主要说的是第三点,以及第五点。
首先第三点现场给出的例子是:
username: zhaolei2
password: 123ZhaoLei2(不允许原因大小写包含用户名)
123zha0leiz(不允许原因存在相似字符或数字)
123leizhao2(不允许原因存在名字拼音倒写)
客户给出的相似字符有
Z,z ----------2
O,o----------0
L,l------------1
当时第一反应看到这个需求是使用正则表达式,但是在我努力的百度以及费劲心思的想关键字怎么百度的情况下还是没有查到对应的解决方案。于是我很快放弃了,决定自己写,
自己写就意味着要写一个算法,能把对应字符替换,我也是这样实现的:
代码如下:
import java.text.SimpleDateFormat;
import java.util.Date;
public class test {
public static void main(String[] args) {
String username = "zhaotianyizhaotianyi".toLowerCase();
String password = "12345678901234567890".toLowerCase();
System.out.println("username:"+username+"password:"+password);
boolean passRole = true;
char similarNumber[]={'z','2','l','1','o','0'};
if(password.equals(username)) {
passRole=false;
}
SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
System.out.println(df1.format(new Date()));
for(int i=0;i<similarNumber.length;i++) {
StringBuffer changeStr = new StringBuffer();
for(int j = 0;j<password.length();j++) {
char c=password.charAt(j);
if(i%2==0) {
if(c==similarNumber[i]) {
c=similarNumber[i+1];
}
}else {
if(c==similarNumber[i]) {
c=similarNumber[i-1];
}
}
changeStr.append(c);
}
System.out.println("password:"+changeStr.toString()+"username2:"+new StringBuffer(username).toString());
if(changeStr.toString().equals((new StringBuffer(username)).toString())) {
passRole=false;
System.out.println("是否符合密码规范:"+passRole);
break;
}
}
SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
System.out.println(df2.format(new Date()));
}
}
写完之后发现,这仅仅是替换一次的算法,还有替换很多个字符的,于是我计算了下,使用数学上的排列组合一共包含的验证方式有
C(6,1)+C(6,2)+C(6,3)+C(6,4)+C(6,5)+C(6,6) = 多少种我也懒得算了,写出了第一种C(6,1)的算法我就放弃了,于是便想更简单的解决方案,重新百度了一遍还是没有找到,忽然想到为什么一定要替换之后在检验呢?于是我想出了:
将用户名与口令中所有形近的数字统一转换成字符 如
username: zhaotianyi2 ------> zhaotianyiz
password: 2ha0tianyi2 ------> zhaotianyiz
之后在进行比对 如果存在密码中存在用户名则口令校验不通过。
代码如下:
public class test1 {
public static void main(String[] args) {
String username = "zhaotianyi2".toLowerCase();
String password = "12haotianyiz2ha0tianyix".toLowerCase();
System.out.println("username:"+username+"password:"+password);
if(password.equals(username)) {
System.out.println("不符合密码规范");
}
StringBuffer stbUsername = new StringBuffer();
StringBuffer stbPassword = new StringBuffer();
for(int j = 0;j<password.length();j++) {
char c=password.charAt(j);
if(c=='0') {
c='o';
}
if(c=='1') {
c='l';
}
if(c=='2') {
c='z';
}
stbPassword.append(c);
}
for(int j = 0;j<username.length();j++) {
char c=username.charAt(j);
if(c=='0') {
c='o';
}
if(c=='1') {
c='l';
}
if(c=='2') {
c='z';
}
stbUsername.append(c);
}
int i = (stbPassword.toString()+"").indexOf(stbUsername.toString());
System.out.println("i:"+i);
if(i!=-1) {
System.out.println("不符合密码规范");
}
}
}
发现简单的很,直接就比对出来了!这样不允许原因大小写包含用户名,存在相似字符或数字,就解决了,对于第三点要求只剩下拼音倒序了(我还是没找到好的方案,问了经理,给出的方案是使用数据字典,我还没弄明白,等弄明白了在更新博客)。
于是就剩下了第五点,口令设置应避免键盘排序密码。
根据以往经验以及百度结果发现java自带一个工具类类名为Pw,我也不知道有没有这个类 反正我是从项目以往的案例中摘取出来的,代码如下:
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
public class Pw
{
public static void main(String[] args)
{
String test = "ehc";
System.out.println(testPW1(test.toLowerCase()));
}
public static boolean testPW(String str)
{
char[][] keyCode = { { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')' },
{ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p' },
{ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';' },
{ 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/' } };
char[] c = str.toCharArray();
List x = new ArrayList();
List y = new ArrayList();
for (int i = 0; i < c.length; ++i) {
char temp = c[i];
label358: for (int j = 0; j < keyCode.length; ++j) {
for (int k = 0; k < keyCode[j].length; ++k) {
if (temp == keyCode[j][k]) {
x.add(Integer.valueOf(j));
y.add(Integer.valueOf(k));
break label358:
}
}
}
}
boolean flag = false;
for (int i = 0; i < x.size() - 2; ++i)
{
if ((x.get(i) == x.get(i + 1)) && (x.get(i + 1) == x.get(i + 2))) {
if (((Integer)y.get(i)).intValue() > ((Integer)y.get(i + 2)).intValue()) {
if ((((Integer)y.get(i)).intValue() - 1 != ((Integer)y.get(i + 1)).intValue()) || (((Integer)y.get(i)).intValue() - 2 != ((Integer)y.get(i + 2)).intValue())) continue;
flag = true;
break;
}
if ((((Integer)y.get(i)).intValue() + 1 != ((Integer)y.get(i + 1)).intValue()) || (((Integer)y.get(i)).intValue() + 2 != ((Integer)y.get(i + 2)).intValue())) continue;
flag = true;
break;
}
if ((x.get(i) != x.get(i + 1)) && (x.get(i + 1) != x.get(i + 2)) && (x.get(i) != x.get(i + 2))) {
if (((Integer)x.get(i)).intValue() > ((Integer)x.get(i + 2)).intValue()) {
if ((((Integer)x.get(i)).intValue() - 1 != ((Integer)x.get(i + 1)).intValue()) || (((Integer)x.get(i)).intValue() - 2 != ((Integer)x.get(i + 2)).intValue()) ||
(y.get(i) != y.get(i + 1)) || (y.get(i) != y.get(i + 2))) continue;
flag = true;
break;
}
if ((((Integer)x.get(i)).intValue() + 1 != ((Integer)x.get(i + 1)).intValue()) || (((Integer)x.get(i)).intValue() + 2 != ((Integer)x.get(i + 2)).intValue()) ||
(y.get(i) != y.get(i + 1)) || (y.get(i) != y.get(i + 2))) continue;
flag = true;
break;
}
}
return flag;
}
public static boolean testPW1(String str)
{
char[][] keyCode = {
{ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' },
{ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p' },
{ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';' },
{ 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/' } };
char[] c = str.toCharArray();
List x = new ArrayList();
List y = new ArrayList();
for (int i = 0; i < c.length; ++i) {
char temp = c[i];
label358: for (int j = 0; j < keyCode.length; ++j) {
for (int k = 0; k < keyCode[j].length; ++k) {
if (temp == keyCode[j][k]) {
x.add(Integer.valueOf(j));
y.add(Integer.valueOf(k));
break label358:
}
}
}
}
boolean flag = false;
for (int i = 0; i < x.size() - 2; ++i)
{
if ((x.get(i) == x.get(i + 1)) && (x.get(i + 1) == x.get(i + 2))) {
if (((Integer)y.get(i)).intValue() > ((Integer)y.get(i + 2)).intValue()) {
if ((((Integer)y.get(i)).intValue() - 1 != ((Integer)y.get(i + 1)).intValue()) || (((Integer)y.get(i)).intValue() - 2 != ((Integer)y.get(i + 2)).intValue())) continue;
flag = true;
break;
}
if ((((Integer)y.get(i)).intValue() + 1 != ((Integer)y.get(i + 1)).intValue()) || (((Integer)y.get(i)).intValue() + 2 != ((Integer)y.get(i + 2)).intValue())) continue;
flag = true;
break;
}
if ((x.get(i) != x.get(i + 1)) && (x.get(i + 1) != x.get(i + 2)) && (x.get(i) != x.get(i + 2))) {
if (((Integer)x.get(i)).intValue() > ((Integer)x.get(i + 2)).intValue()) {
if ((((Integer)x.get(i)).intValue() - 1 != ((Integer)x.get(i + 1)).intValue()) || (((Integer)x.get(i)).intValue() - 2 != ((Integer)x.get(i + 2)).intValue()) ||
(y.get(i) != y.get(i + 1)) || (y.get(i) != y.get(i + 2))) continue;
flag = true;
break;
}
if ((((Integer)x.get(i)).intValue() + 1 != ((Integer)x.get(i + 1)).intValue()) || (((Integer)x.get(i)).intValue() + 2 != ((Integer)x.get(i + 2)).intValue()) ||
(y.get(i) != y.get(i + 1)) || (y.get(i) != y.get(i + 2))) continue;
flag = true;
break;
}
}
return flag;
}
}
调用Pw.testPW(newPassWord.toLowerCase())方法即可校验。
如上整个分析过程及解决方案,很喜欢这种动脑思考的问题,只有亲自经历过才能体会到那种兴奋。