From 8c370af3f14d70643e346903b268089e6c1e9475 Mon Sep 17 00:00:00 2001 From: "abel.liu" Date: Wed, 21 Jun 2023 15:16:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E6=96=B0=E5=AF=86=E7=A0=81=E6=97=B6=E5=BC=BA=E5=BA=A6=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logic/user_logic.go | 6 +++ public/tools/check_passwd.go | 84 ++++++++++++++++++++++++++++++++++++ public/tools/util_test.go | 50 +++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 public/tools/check_passwd.go diff --git a/logic/user_logic.go b/logic/user_logic.go index 1fcdea5..884640d 100644 --- a/logic/user_logic.go +++ b/logic/user_logic.go @@ -339,6 +339,12 @@ func (l UserLogic) ChangePwd(c *gin.Context, req interface{}) (data interface{}, if tools.NewParPasswd(user.Password) != r.OldPassword { return nil, tools.NewValidatorError(fmt.Errorf("原密码错误")) } + + err = tools.CheckPasswdStrength(r.NewPassword) + if err != nil { + return nil, tools.NewValidatorError(err) + } + // ldap更新密码时可以直接指定用户DN和新密码即可更改成功 err = ildap.User.ChangePwd(user.UserDN, "", r.NewPassword) if err != nil { diff --git a/public/tools/check_passwd.go b/public/tools/check_passwd.go new file mode 100644 index 0000000..19b81d7 --- /dev/null +++ b/public/tools/check_passwd.go @@ -0,0 +1,84 @@ +package tools + +import ( + "fmt" + "strings" + "unicode" +) + +/* +新密码规则: +1. 8位(包含)-16位(包含) +2. 包含至少一个拉丁大写字母 +3. 包含至少一个拉丁小写字母 +4. 包含至少一个数字 +5.包含至少一个特殊字符 !@#%^&*()-_+={}][|;:<>,.? +6. 密码不能包含空格,特殊unicode字符 +*/ + +// 校验新密码强度是否符合规则 +func CheckPasswdStrength(newPasswd string) (err error) { + // 校验密码长度 + if len(newPasswd) < 8 || len(newPasswd) > 16 { + return fmt.Errorf("密码长度不符合规则") + } + + // flags[0] hasUpper + // flags[1] hasLower + // flags[2] hasNumber + // flags[3] hasSpecial + var flags = make([]bool, 4) + + // 以unicode字符形式遍历 + for _, u := range newPasswd { + // 密码不能包含空格 + if unicode.IsSpace(u) { + return fmt.Errorf("密码不能包含空格") + } + + // 如果既不是大小写字母,也不是数字,也不是标点符号 + // 为什么不用unicode.IsLetter()是为了排除如'æ'拉丁字母的干扰 + if !isLetter(u) && !unicode.IsNumber(u) && !isSpecial(u) { + return fmt.Errorf("密码不能包含Unicode特殊字符") + } + + // 判断是否为大写字母 + if unicode.IsUpper(u) { + flags[0] = true + } + + // 判断是否为小写字母 + if unicode.IsLower(u) { + flags[1] = true + } + + // 判断是否为数字 + if unicode.IsNumber(u) { + flags[2] = true + } + + // 判断特殊字符 + if isSpecial(u) { + flags[3] = true + } + } + + // 判断四个条件是否满足 + for _, flag := range flags { + if !flag { + return fmt.Errorf("密码不符合规则") + } + } + + return nil +} + +// 判断特殊字符 +func isSpecial(u rune) bool { + return strings.Contains("!@#%^&*()-_+={}][|;:<>,.?", string(u)) +} + +// 判断是否为拉丁字母 +func isLetter(u rune) bool { + return (u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z') +} diff --git a/public/tools/util_test.go b/public/tools/util_test.go index 4ebfe4e..332f245 100644 --- a/public/tools/util_test.go +++ b/public/tools/util_test.go @@ -1,6 +1,7 @@ package tools import ( + "errors" "fmt" "testing" ) @@ -37,3 +38,52 @@ func TestEncodePass(t *testing.T) { fmt.Println("its not match") } } + +// 测试密码强度是否符合规则 +func TestNewPasswdStrength(t *testing.T) { + tests := []struct { + name string + pwd string + err error + }{ + // TODO: Add test cases. + {name: "[False]长度不足8", pwd: "abc@!Q", err: errors.New("pasword invalid")}, + {name: "[False]长度大于16", pwd: "abc@!Qaaassss3729128aaaaaaaaa", err: errors.New("pasword invalid")}, + {name: "[False]包含空格", pwd: "abAa a32!aCa", err: errors.New("pasword invalid")}, + {name: "[False]全大写", pwd: "AAGGGAAGAAAAAA", err: errors.New("pasword invalid")}, + {name: "[False]全小写", pwd: "abcabcabc", err: errors.New("pasword invalid")}, + {name: "[False]全数字", pwd: "111111111", err: errors.New("pasword invalid")}, + {name: "[False]全特殊字符", pwd: "!@#$%^&*(()_+", err: errors.New("pasword invalid")}, + + {name: "[False]大写+小写", pwd: "abcabcQQQQ", err: errors.New("pasword invalid")}, + {name: "[False]大写+数字", pwd: "1111QQQQ", err: errors.New("pasword invalid")}, + {name: "[False]大写+特殊字符", pwd: "QQQQA(())", err: errors.New("pasword invalid")}, + {name: "[False]小写+数字", pwd: "abcabc7890", err: errors.New("pasword invalid")}, + {name: "[False]小写+特殊字符", pwd: "abcabc.(())", err: errors.New("pasword invalid")}, + {name: "[False]数字+特殊字符", pwd: "12345678&()", err: errors.New("pasword invalid")}, + {name: "[False]大写+小写+数字", pwd: "AAcabc7890", err: errors.New("pasword invalid")}, + {name: "[False]大写+小写+特殊字符", pwd: "aBcabcQ(A)Q", err: errors.New("pasword invalid")}, + {name: "[False]小写+数字+特殊字符", pwd: "abc1239)))", err: errors.New("pasword invalid")}, + + {name: "[False]unicode特殊字符#00", pwd: "æææææAb#c1", err: errors.New("pasword invalid")}, + {name: "[False]unicode特殊字符#01", pwd: "\b5Ὂg̀9! ℃ᾭG", err: errors.New("pasword invalid")}, + {name: "[False]unicode特殊字符#02", pwd: "中文是密码Ý", err: errors.New("pasword invalid")}, + {name: "[False]unicode特殊字符#03", pwd: "notEng3.14Ý", err: errors.New("pasword invalid")}, + {name: "[False]unicode特殊字符#04", pwd: "ĒNĜĹis Ĥ", err: errors.New("pasword invalid")}, + {name: "[False]unicode特殊字符#05", pwd: "😂😂😂😂😂", err: errors.New("pasword invalid")}, + + {name: "[True]大写-小写-数字-特殊字符", pwd: "AbcFAc.163606", err: nil}, + {name: "[True]大写-小写-数字-特殊字符", pwd: "AbcP1bc!!#.#", err: nil}, + {name: "[True]大写-小写数字-特殊字符", pwd: "AUbEXZ#14159", err: nil}, + {name: "[True]大写-小写-数字-特殊字符", pwd: "iyTmqp@14159", err: nil}, + {name: "[True]大写-小写-数字-特殊字符", pwd: "G1thub:liuup", err: nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := CheckPasswdStrength(tt.pwd) + if (err != nil && tt.err == nil) || (err == nil && tt.err != nil) { + t.Errorf("CheckPasswdStrength() error = %v", err) + } + }) + } +}