这是我的毕业设计(一个名叫viBlogging的blog系统)中需要的一个功能:在用户注册时根据输入的密码来显示密码强度(模仿passport的,不过是使用Atlas)。
对于密码强度,分为弱、中、强三等级,我的判断规则是:
1。如果密码的位数在3~4位之间,如果只含有字母,那么强度为弱。
2。如果密码的位数在3~4位之间,,如果还含有数字,那么强度也为弱。
3。如果密码的位数在3~4位之间,如果含有非字母数字的字符,那么强度为中。
4。如果密码位数在5~7之间,只含有字母的强度为弱
5。如果密码位数在5~7之间,同时还有数字的强度为中。
6。如果密码位数在5~7之间,同时还有非字母数字字符的强度为强。
7。如果密码长度大于7,如果只含有字母,那么强度为中。
8。如果密码长度大于7,如果还含有数字的话强度为中。
9。如果密码长度大于7,如果含有非字母数字字符的话,强度为强。
如果此时昨晚规则的分析后就着手写代码的话,那要写一堆if嵌套了。这样做的话,很可能会使得逻辑陷入混乱,很有可能一些情况就被忽略过去了。更为严重的是,这样写严重降低了代码的可读性。如果我必须用这样的方式来写,那我首先会去写unit test,其次再附详细的doc或注释来说明。
Okay,那我们进一步来分析一下。我们上述的9个条件都是由密码长度和密码复杂度两部分来结合判断出来的。那么我们可以拆分一下,如下图所示(请大家先只看第一个表格):(我点"insert image from gallery"后显示访问的资源不存在,无法从博客园这插入图片了,所以在首页可能看不到有图片)

我们现在写两个函数,分别计算长度和复杂度对应的强度,并返回。这里我们用{0,1,2}和{0,1,3}来表示(为什么后者是{0,1,3}在后面会讲到)那么我们可以很容易把上面的9个规则总结成第一个表格来表示。
到这里为止,我可以分别使用i和j来表示拆分后的强度的表示,好,现在我们可以去写清晰一点的if..else嵌套或if..switch嵌套了,这里我们可以用标识符了,采用了简单的分而治之的思想,整个规则也被简化为了7步了(读者可以自己考虑一下),思路清晰了许多。
但是目前为止,我们还是没有脱离嵌套条件分支语句的尴尬和不便。
Okay,我们继续对这个逻辑的返回结果作分析:
强度:情况1,情况2 = {(i1,j1),(i2,j2),....} § [(i+j)min,(i+j)max]
Strong: F,I = {(!0,3)} § [4,5]
Medium: G,E,H,C = {(2,0),(!0,1),(3,0)} § [2,3]
Weak: A,B,D = {(0,!3),(1,0)} § [0,1]
好,这样结果就很清楚了,当返回的i+j在>3时,那么强度肯定是strong的,如果小于2,那么肯定是weak的,剩下的就是Medium的了。这样只要写一个switch就完成任务了(我只在有双分支的时候用if,2个以上的都用switch,学过编译原理的应该知道它比if效率高,可是.net中他俩有什么性能区别我不知道,但是如果不出意外,应该还是比if效率高吧)。
此文仅在提供一种分解问题和归类问题各部分结果的小方法,当然也有“运气成分”(这就是为什么用{0,1,3}不用{0,1,2}的原因)。抛砖引玉,希望能学到更好的解决办法。
posted on 2006-05-13 01:40
维生素C.NET 阅读(3619)
评论(28) 编辑 收藏 网摘 所属分类:
ASP.NET
发表评论
1,得到长度
2,依次匹配三个正则
3,根据业务逻辑付值并返回.
@Echo.chen
@极地银狐.NET
使用正则表达式也是要写一定的逻辑判断的.此文的方法仅在利用简单的分治思想来细分逻辑,减少分支语句的使用.
弱:l=".{3,4}" 中:m="这里先不写了" 高:h
string [] arrSExep = {h,m,l}
string [] arrValue = {"高","中","低"}
for(int i=0; i<arrSExep.length; i++)
{
if(Exp.Match(password,arrSExep[i]))
return arrValue[i];
}
大致写法应该如此
当然数组可以写一个二维的
嗯,思路不错
但是我还是回几句,遇到这样的问题,就想用if套,这就是错误的,我把我的想法写下吧。
string GetLevel(string password)
{
Hashtable levelMapping = new Hashtable();
Regex regex1 = new("略了");
levelMapp.Add(regex1, "弱");
.
.
.
Regex regex9 = new("略了");
levelMapp.Add(regex9, "强");
foreach (Regex regex in levelMapping.Keys)
{
if (regEx.IsMatch(password)) return levelMapping[regex];
}
}
用查表的方法,很好!其实表的概念是计算机技术里很重要的一种思想,表的概念易于优化,代码精炼,如果优化的好,可以做到空间复杂度和时间复杂度都很低。不过就是难于编写文档和维护,如果没有好的文档,别人维护起来会比较痛苦,尤其是经过优化的。
嗯~学习。
有时候换种思维模式来思考同一个问题,会得到完全不同的解决方案。
我倒是觉得计算密码的每个字符的编码值所在的范围再逐字加上相应的权重,这样的话,算法很简单,而且也更符合实际的问题逻辑
32~47,58~64,91~96 为符号
65~90,97~122为字母
48~57为数字
分别加不同的权重,最后的结果值决定级别,再加上有2种+10的权重,有3种加20的权重之类的
@榛樼煶
string [] arrSExep = {h,m,l}
这个的逻辑怎么判断呢?
@阮
这样做的话似乎扩充性就不太高了
@Anders Liu (lover_P)
博哥说的是,就像这个逻辑,如果不深入去看去分析的话,但看列出的逻辑虽能明白,但是如果做扩充还是很有你难度的,是不太好理解.谢谢博哥指点:-)
@THIN
Thin哥,由于水平问题,看不太明白您的意思,可否给个简单的代码实现?
@维生素C.NET
我简单解释一下:
首先将问题简化一下:密码只允许26个小写字母和10个数字,假设只有字母为弱,二者都有为强。(同时密码长度不会超过100位)
int sum = 0;
foreach(char c in password char array){
if (c is english char){
sum += 1;
}
else if(c is digital){
sum += 100;
}
}
if sum < 100 return week;
if sum > 100 return strong;
上述算法只需要O(n)的复杂度,是最快的。
但是考虑到复杂情况,为每种情况指定合适的权值不会很容易,甚至有时是不可能的,特别是有复杂逻辑的情况下。
@Dflying Chen
那我返回的{0,1,3}这样的算不算权值呢?
之所以我会提出这样的算法,是我一贯认为程序应首先反映现实逻辑,在看待一个密码是否强时,人们一般会把密码从头到尾看一遍,看看只否有多种组成,看看是否够长,所以我会提出从头到尾检查整个密码,得出最后的强度指数的算法,下面的算法我最终没有用这种步骤是因为在考虑了人的思维逻辑之后要迁就机器思维,这样写也许代码比较简洁,一点不成熟的想法,希望对你有帮助,:),考虑到用C#写更简单,我就用JS实现了,代码如下。
<script type="text/javascript">
var PasswordStrength ={
HightLevel : 30,//强
MidLevel : 20,//中
LowLevel : 0,//弱
Factor : [1,2,5],//字符加数,分别为字母,数字,其它
KindFactor : [0,0,10,20],//密码含几种组成的加数
Regex : [/[a-zA-Z]/g,/\d/g,/[^a-zA-Z0-9]/g] //字符正则数字正则其它正则
}
PasswordStrength.StrengthValue = function(pwd)
{
var strengthValue = 0;
var ComposedKind = 0;
for(var i = 0 ; i < this.Regex.length;i++)
{
var chars = pwd.match(this.Regex[i]);
if(chars != null)
{
strengthValue += chars.length * this.Factor[i];
ComposedKind ++;
}
}
strengthValue += this.KindFactor[ComposedKind];
return strengthValue;
}
PasswordStrength.StrengthLevel = function(pwd)
{
var value = this.StrengthValue(pwd);
if( value >= this.HightLevel)
return "高,实在是高";
if( value >= this.MidLevel)
return "还行啦";
else
return "靠,这样也行";
}
alert(PasswordStrength.StrengthLevel("23"));
alert(PasswordStrength.StrengthLevel("abcd123"));
alert(PasswordStrength.StrengthLevel("abcd!%23"));
</script>
另外,"希望可以认识您,多像您学习.",学习不敢当的,我的MSN是thin37421@hotmail.com
为了灵活支持强度等级,还可以重构一下:
<script type="text/javascript">
var PasswordStrength ={
Level : ["高,实在是高","还行啦","靠,这样也行"],
LevelValue : [30,20,0],//强度值
Factor : [1,2,5],//字符加数,分别为字母,数字,其它
KindFactor : [0,0,10,20],//密码含几种组成的加数
Regex : [/[a-zA-Z]/g,/\d/g,/[^a-zA-Z0-9]/g] //字符正则数字正则其它正则
}
PasswordStrength.StrengthValue = function(pwd)
{
var strengthValue = 0;
var ComposedKind = 0;
for(var i = 0 ; i < this.Regex.length;i++)
{
var chars = pwd.match(this.Regex[i]);
if(chars != null)
{
strengthValue += chars.length * this.Factor[i];
ComposedKind ++;
}
}
strengthValue += this.KindFactor[ComposedKind];
return strengthValue;
}
PasswordStrength.StrengthLevel = function(pwd)
{
var value = this.StrengthValue(pwd);
for(var i = 0 ; i < this.LevelValue.length ; i ++)
{
if(value >= this.LevelValue[i] )
return this.Level[i];
}
}
alert(PasswordStrength.StrengthLevel("23"));
alert(PasswordStrength.StrengthLevel("abcd123"));
alert(PasswordStrength.StrengthLevel("abcd!%23"));
</script>
关于搂主“运气成分(这就是为什么用{0,1,3}不用{0,1,2}的原因)”的说法我不太同意。根据本问题的需求完全可以推导出{0,1,3}。其实{0,1,x}x>2都是可以的。
@dragonpig
一开始我的想法很自然的就是对密码长度和密码复杂度的判断返回的都是{0,1,2}后来在总结规律的时候发现把对密码复杂度的返回改为{0,1,3}后可以直接通过加运算给三种强度“划清界限”。
您是如何推到出这个返回值的呢?请教一下判断的思路。
@THIN
谢谢Thin.我昨天实现了一下遇到了点问题,不熟悉正则表达式.今天继续完善一下.
不过有一个地方,就是特殊字符我认为范围应该是在33-47,58-64,91-96,123-126之间.
程序的复杂度判断O()应该是程序中个部分的最高复杂度,很失败的是这个地方我给忘了...
可以这么实现
数字系数为8
字母系数为11
其他符号系数为25
比如密码为:
123 安全系数为24
abcd 安全系数为44
12@3 安全系数为49
123566 安全系数为48
a3#3b6 安全系数为72
然后根据0-(低)-45-(中)-75(高)划分
@最笨的那个
您这样的思路我认为是不正确的。8位数字组成的密码其实是不安全的(用MD5对照表来穷举破解也不用太多时间),那么这样看来它的安全度应该是8*8=64>45=中,而如果是a&1~这个密码的话系数为69<75=中。但是后者的密码强度远远比前者要高。
也就是说,这个系数一定要经过数学推理出来才可以,如果只是任取,那么我认为这套规则是盲目的。
@最笨的那个
其实这想法不错,就是系数值得商榷
考虑到穷举破解时若每一位要尝试n个值,总共k位,就一共有n^k种情况,那么我们可以让每一位的权值为log(n),这样权值之和就表示了密码的难度
数字:log(10) = 1
字母:log(52) = 1.716
字母和数字:log(62) = 1.792
纯符号:log(29) = 1.462
字母、数字、符号:log(91) = 1.959
考虑的很好,简化了程序代码;
只是应该考虑大写字母