1 /** 2 * 密码强度检测 3 * 4 * @fileOverView 基础组件 5 * @author <a href="mailto:zhang.gd@foxmail.com">Zhang Guangda</a> 6 * @date 2012-10-25 7 */ 8 define(function() { 9 10 /* 11 * SNDA 密码强度检测对象 12 * Level = 0 13 * 错误:errorId 意义 14 * 1 长度、字符不符合 15 * 2 密码属于社会工程学中 16 * 3 密码与用户名相同 17 * 4 连续或重复的数字 18 * 5 连续或重复的字母 19 */ 20 function PasswordStrength() 21 { 22 this.minLen = 6; 23 this.maxLen = 30; 24 this.pwdCharSet="A-Za-z0-9"; 25 this.caseInsensitive=false; 26 this.Description=""; 27 this.userName=""; 28 this.applyRulesDesc=new Array(); 29 this.errorId="0"; 30 } 31 32 PasswordStrength.prototype.$ = function(s) { 33 return document.getElementById(s); 34 } 35 36 PasswordStrength.prototype.setMinLen = function(n){ 37 if(isNaN(n)) 38 { 39 return ; 40 } 41 n = Number(n); 42 if(n>1) 43 { 44 this.minLen = n; 45 } 46 } 47 PasswordStrength.prototype.setMaxLen = function(n){ 48 if(isNaN(n)) 49 { 50 return ; 51 } 52 n = Number(n); 53 if(n >=this.minLen) 54 { 55 this.maxLen = n; 56 } 57 } 58 PasswordStrength.prototype.setPwdCharSet = function(s){ 59 this.pwdCharSet=s; 60 } 61 PasswordStrength.prototype.getDescription =function() { 62 return this.Description; 63 } 64 //大小写敏感 65 PasswordStrength.prototype.setCaseInsensitive = function(s){ 66 this.caseInsensitive=s; 67 } 68 PasswordStrength.prototype.setUserName = function(s) { 69 this.userName=s; 70 } 71 PasswordStrength.prototype.getApplyRulesDesc =function () { 72 return this.applyRulesDesc; 73 } 74 PasswordStrength.prototype.getErrorId = function (){ 75 return this.errorId; 76 } 77 78 //检查密码的长度和字符是否满足 79 PasswordStrength.prototype.checkPwd = function(s) { 80 var lvRegExp=new RegExp("^["+this.pwdCharSet+"]{"+this.minLen+","+this.maxLen+"}$",this.caseInsensitive?"":"i"); 81 return lvRegExp.test(s); 82 } 83 //检查密码是否是社会工程字典中的密码 84 PasswordStrength.prototype.checkDict = function(s) { 85 var lvDicts=new Array("shanda","asdfg","asdfgh","qwert","qwerty","zxcvb","zxcvbn", 86 "Password","Passwd","Woaini","Iloveyou","Woaiwojia","521521","5201314","7758521","1314520","1314521", 87 "520520","201314","211314","7758258","147258369","159357", 88 "test","snda","12345","123456","1234567","12345678","123456789","asdf","qwer","zxcv","654321","123123", 89 "123321","123abc"); 90 // var lvDicts=new Array(); 91 if(this.caseInsensitive==false) 92 { 93 s=s.toUpperCase(); 94 } 95 for(var i=0;i<lvDicts.length;i++) 96 { 97 if(s==(this.caseInsensitive?lvDicts[i]:lvDicts[i].toUpperCase())) 98 { 99 return s; 100 } 101 } 102 return ""; 103 } 104 PasswordStrength.prototype.getLevel = function(s) 105 { 106 this.applyRulesDesc=new Array(); 107 this.errorId="0"; 108 this.Description=""; 109 if(this.checkPwd(s)) 110 { 111 if(this.checkDict(s)=="") 112 { 113 //密码与用户名是否相同 114 var lvUserName=this.userName; 115 var lvPwd=s; 116 if(lvUserName == null) 117 { 118 lvUserName=""; 119 } 120 if(this.caseInsensitive==false) 121 { 122 lvUserName=lvUserName.toUpperCase(); 123 lvPwd=lvPwd.toUpperCase(); 124 } 125 if(lvUserName==lvPwd) 126 { 127 this.errorId="3"; 128 this.applyRulesDesc.push("规则 2: 密码与用户名相同"); 129 //this.Description="密码与用户名相同"; 130 this.Description="对不起,请勿使用与用户名相同的密码"; 131 return 0; 132 } 133 //add rule: half of pwd can not be exist in username 134 var lvHalfPwdLen=Math.floor((lvPwd.length+1)/2); 135 for(var lvPwdIndex=0,lvLoopIndex=lvPwd.length-lvHalfPwdLen;lvPwdIndex<=lvLoopIndex;lvPwdIndex++) 136 { 137 var lvCompHalfPwd=lvPwd.substring(lvPwdIndex,lvHalfPwdLen+lvPwdIndex); 138 if(lvUserName.indexOf(lvCompHalfPwd)>=0) 139 { 140 this.applyRulesDesc.push("规则 20: 密码中的字符与用户名的字符不能有半数相同"); 141 //this.Description="密码中的字符与用户名的字符有半数相同"; 142 this.Description="对不起,请不要将账号的一部分作为密码"; 143 return 0; 144 } 145 } 146 147 var ls = 0; 148 if( (this.caseInsensitive? s.match(/[a-z]/g): s.match(/[a-z]/ig))) 149 { 150 this.applyRulesDesc.push("规则 4: 密码包含字母,权重加1"); 151 ls++; 152 } 153 if (s.match("[0-9]","ig")) 154 { 155 this.applyRulesDesc.push("规则 5: 密码含有数字,权重加1"); 156 ls++; 157 } 158 if (s.length < 6 && ls > 1) 159 { 160 this.applyRulesDesc.push("规则 7: 密码长度小于6且权重大于1,权重减1"); 161 ls--; 162 } 163 if (s.length >= 8 && ls == 2) 164 { 165 this.applyRulesDesc.push("规则 8: 密码长度大于等于8且权重等于2,权重加1"); 166 ls++; 167 } 168 if (ls > 1 && !this.samePwd(s)) 169 { 170 this.applyRulesDesc.push("规则 9: 连续重复的数字/字母,权重减1"); 171 ls--; 172 } 173 if (ls > 1 && !this.increasePwd(s) ) 174 { 175 this.applyRulesDesc.push("规则 12: 连续递增的数字(仅仅是数字)长度占到总密码长度的50%以上,权重减1"); 176 ls--; 177 } 178 if (ls > 1 && !this.descendingPwd(s)) 179 { 180 this.applyRulesDesc.push("规则 12: 连续递减的数字(仅仅是数字)长度占到总密码长度的50%以上,权重减1"); 181 ls--; 182 } 183 if(ls==1 && this.checkDigitial(s)) //连续/重复数字 权重-1 184 { 185 this.errorId="4" 186 this.applyRulesDesc.push("规则 10: 当密码中全是连续/重复的数字,权重减1"); 187 //this.Description="密码为连续或重复的数字"; 188 this.Description="对不起,请不要将连续或重复的数字作为密码"; 189 ls--; 190 } 191 if(ls==1 && this.checkCharLetter(s)) //连续/重复字母 权重-1 192 { 193 this.errorId="5" 194 this.applyRulesDesc.push("规则 10: 当密码中全是连续/重复的字母,权重减1"); 195 //this.Description="密码为连续或重复的字母"; 196 this.Description="对不起,请不要以连续或重复的字母作为密码"; 197 ls--; 198 } 199 if(ls>1 && this.checkDigitialCharLetters(s)) //密码包含字母和数字,且字母和数字皆连续,权重-1 200 { 201 this.applyRulesDesc.push("规则 11: 密码包含字母和数字,且字母和数字皆连续,权重减1"); 202 ls--; 203 } 204 if(ls==2 && this.checkCharLetterCounts(s,1)) //一个字母和数字组合情况,权重-1 205 { 206 this.applyRulesDesc.push("规则 14: 权重为2,只有一种字母与数字组合,权重减1"); 207 ls--; 208 } 209 if(ls==3 && this.checkCharLetterCounts(s,2)) //小于等于2个字母和数字组合情况,权重-1 210 { 211 this.applyRulesDesc.push("规则 14: 权重为3,只有一种或二种字母与数字组合,权重减1"); 212 ls--; 213 } 214 return ls; 215 } 216 else 217 { 218 this.errorId="2"; 219 this.applyRulesDesc.push("规则 1:密码属于符合社会工程学词典"); 220 //this.Description="你不能使用"+s+"作为密码,该密码非常容易被猜测!"; 221 this.Description="对不起,您的密码过于简单"; 222 //return 0; 223 return 1; 224 } 225 } 226 else 227 { 228 this.errorId="1"; 229 this.applyRulesDesc.push("规则 3:密码长度必须在["+this.minLen+","+this.maxLen+"],且密码字符必须在["+this.pwdCharSet+"]"); 230 //this.Description="密码长度必须在["+this.minLen+","+this.maxLen+"]之间,且密码字符必须在["+this.pwdCharSet+"]"; 231 this.Description="对不起,密码只能由"+this.minLen+"-"+this.maxLen+"位数字和字母组成"; 232 return 0; 233 } 234 } 235 PasswordStrength.prototype.checkCharLetterCounts = function(p,pvCounts) { 236 var s=p.toUpperCase(); 237 if(this.caseInsensitive) 238 { 239 s=p; 240 } 241 var lvRegExp=new RegExp("^[A-Za-z]$",""); 242 var lvLastChar=""; 243 var lvCharCounts=0; 244 for(var i=0;i<s.length;i++) 245 { 246 var c=s.substring(i,i+1); 247 if(lvRegExp.test(c)) 248 { 249 if(lvLastChar.indexOf(c)==-1) 250 { 251 lvCharCounts++; 252 lvLastChar=lvLastChar+c; 253 } 254 } 255 } 256 return (lvCharCounts<=pvCounts); 257 } 258 PasswordStrength.prototype.convertCharLetterInt= function(p){ 259 var s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 260 for(var i=0;i<s.length;i++) 261 { 262 if(s.substring(i,i+1)== (this.caseInsensitive?p:p.toUpperCase())) 263 { 264 return i; 265 } 266 } 267 return -1; 268 } 269 PasswordStrength.prototype.checkDigitialCharLetters = function(p){ 270 var s=p.toUpperCase(); 271 if(this.caseInsensitive) 272 { 273 s=p; 274 } 275 var c=s.substring(0,1); 276 var lvRegExp=new RegExp("^[0-9]$",""); 277 var lvDigitialChar=""; 278 var lvCharLetter=""; 279 var lvNeedChecking=true; 280 if(lvRegExp.test(c)) //数字开始 281 { 282 lvDigitialChar=c; 283 var lvDigitialing=true; 284 for(var i=1;i<s.length;i++) 285 { 286 c=s.substring(i,i+1); 287 if(lvDigitialing) 288 { 289 if(lvRegExp.test(c)) 290 { 291 lvDigitialChar=lvDigitialChar+c; 292 } 293 else 294 { 295 lvDigitialing=false; 296 } 297 } 298 else 299 { 300 if(lvRegExp.test(c)) 301 { 302 lvNeedChecking=false; 303 break; 304 } 305 } 306 } 307 if(lvNeedChecking) 308 { 309 lvCharLetter=s.substring(lvDigitialChar.length,s.length); 310 } 311 } 312 else //字符开始 313 { 314 lvCharLetter=c; 315 var lvCharLettering=true; 316 for(var i=1;i<s.length;i++) 317 { 318 c=s.substring(i,i+1); 319 if(lvCharLettering) 320 { 321 if(lvRegExp.test(c)==false) 322 { 323 lvCharLetter=lvCharLetter+c; 324 } 325 else 326 { 327 lvCharLettering=false; 328 } 329 } 330 else 331 { 332 if(lvRegExp.test(c)==false) 333 { 334 lvNeedChecking=false; 335 break; 336 } 337 } 338 } 339 if(lvNeedChecking) 340 { 341 lvDigitialChar=s.substring(lvCharLetter.length,s.length); 342 } 343 } 344 return (lvNeedChecking && this.checkDigitial(lvDigitialChar) && this.checkCharLetter(lvCharLetter)); 345 } 346 PasswordStrength.prototype.checkCharLetter= function(p){ 347 var s=p.toUpperCase(); 348 if(this.caseInsensitive) 349 { 350 s=p; 351 } 352 var lvLastChar=s.substring(0,1); 353 var lvLetterDiff=0; 354 if(isNaN(lvLastChar)) 355 { 356 for(var i=1;i<s.length;i++) 357 { 358 var c=s.substring(i,i+1); 359 if(isNaN(c)) 360 { 361 if(i==1) 362 { 363 lvLetterDiff=this.convertCharLetterInt(c)-this.convertCharLetterInt(lvLastChar); 364 if(lvLetterDiff!=0 && lvLetterDiff!=1 && lvLetterDiff!=-1) 365 { 366 return false; 367 } 368 } 369 else 370 { 371 if((this.convertCharLetterInt(c)-this.convertCharLetterInt(lvLastChar))!=lvLetterDiff) 372 { 373 return false; 374 } 375 376 } 377 lvLastChar=c; 378 } 379 else 380 { 381 return false; 382 } 383 } 384 return true; 385 } 386 else 387 { 388 return false; 389 } 390 } 391 PasswordStrength.prototype.checkDigitial = function(s){ 392 var lvLastChar=s.substring(0,1); 393 var lvDigitialDiff=0; 394 if(isNaN(lvLastChar)==true) 395 { 396 return false; 397 } 398 for(var i=1;i<s.length;i++) 399 { 400 var c=s.substring(i,i+1); 401 if(isNaN(c)==false) 402 { 403 if(i==1) 404 { 405 lvDigitialDiff=parseInt(c)-parseInt(lvLastChar); 406 if(lvDigitialDiff!=0 && lvDigitialDiff!=1 && lvDigitialDiff!=-1) 407 { 408 return false; 409 } 410 } 411 else 412 { 413 if((parseInt(c)-parseInt(lvLastChar))!=lvDigitialDiff) 414 { 415 return false; 416 } 417 } 418 lvLastChar=c; 419 } 420 else 421 { 422 return false; 423 } 424 } 425 return true; 426 } 427 PasswordStrength.prototype.samePwd = function(s) 428 { 429 if(this.caseInsensitive==false) 430 { 431 s=s.toUpperCase(); 432 } 433 var errorLength = 0; 434 var tmpLength = 1; 435 var i = 0; 436 for(i=1;i<s.length;i++) 437 { 438 if(!isNaN(s.substr(i,1)) && !isNaN(s.substr(i-1,1))) 439 { 440 if(s.substr(i,1) == s.substr(i-1,1)) 441 { 442 tmpLength++; 443 } 444 else 445 { 446 tmpLength = 1; 447 } 448 if(tmpLength > errorLength) 449 { 450 errorLength = tmpLength; 451 } 452 } 453 } 454 if(errorLength>=(s.length/2)) 455 { 456 return false; 457 } 458 else 459 { 460 return true; 461 } 462 } 463 PasswordStrength.prototype.increasePwd = function(s) 464 { 465 var errorLength = 0; 466 var tmpLength = 1; 467 var i = 0; 468 for(i=1;i<s.length;i++) 469 { 470 if(!isNaN(s.substr(i,1)) && !isNaN(s.substr(i-1,1))) 471 { 472 if(Number(s.substr(i,1)) == Number(s.substr(i-1,1)) + 1) 473 { 474 tmpLength++; 475 } 476 else 477 { 478 tmpLength = 1; 479 } 480 if(tmpLength > errorLength) 481 { 482 errorLength = tmpLength; 483 } 484 } 485 } 486 if(errorLength>=(s.length/2)) 487 { 488 return false; 489 } 490 else 491 { 492 return true; 493 } 494 } 495 PasswordStrength.prototype.descendingPwd = function(s) 496 { 497 var errorLength = 0; 498 var tmpLength = 1; 499 var i = 0; 500 for(i=1;i<s.length;i++) 501 { 502 if(!isNaN(s.substr(i,1)) && !isNaN(s.substr(i-1,1))) 503 { 504 if(Number(s.substr(i,1)) == Number(s.substr(i-1,1)) - 1) 505 { 506 tmpLength++; 507 } 508 else 509 { 510 tmpLength = 1; 511 } 512 if(tmpLength > errorLength) 513 { 514 errorLength = tmpLength; 515 } 516 } 517 } 518 if(errorLength>=(s.length/2)) 519 { 520 return false; 521 } 522 else 523 { 524 return true; 525 } 526 } 527 528 var instance = new PasswordStrength(); 529 $we.pwdStrength = function(pwd, username) { 530 instance.setUserName(username || ""); 531 var level = instance.getLevel(pwd); 532 return { 533 errno: parseInt(instance.errorId), 534 level: parseInt(level), 535 msg: instance.Description || "" 536 }; 537 }; 538 539 return $we.pwdStrength; 540 }); 541