使用正则表达式验证密码

时间:2014-09-14 19:35:52

标签: regex go

我正在尝试使用regexp编写密码验证功能,并且不知道如何操作。 Golang正则表达式与其他语言不同。有人知道,这个正则表达式应该是什么样的? 该模式应验证:

/*
 * Password rules:
 * at least 7 letters
 * at least 1 number
 * at least 1 upper case
 * at least 1 special character
 */

5 个答案:

答案 0 :(得分:14)

这实际上是不可能的,因为Go的regex不支持回溯。

然而,它很容易实现,一个简单的例子:

func verifyPassword(s string) (sevenOrMore, number, upper, special bool) {
    letters := 0
    for _, c := range s {
        switch {
        case unicode.IsNumber(c):
            number = true
        case unicode.IsUpper(c):
            upper = true
            letters++
        case unicode.IsPunct(c) || unicode.IsSymbol(c):
            special = true
        case unicode.IsLetter(c) || c == ' ':
            letters++
        default:
            //return false, false, false, false
        }
    }
    sevenOrMore = letters >= 7
    return
}

答案 1 :(得分:7)

正确的正则表达式是......这里没有正则表达式。

您可以定义一个验证密码的自定义函数,并将其与其他框架结合使用,以帮助验证字段,例如mccoyst/validate(在此discussion about parameter validation中提到)

您还有go-validator/validator允许定义类似的验证(但我仍然会使用自定义验证器而不是一个或多个正则表达式。)


注意:go regexp基于 re2 ,这是一个高效的,有原则的正则表达式库)。

  

所以主要的权衡是没有反向引用,例如:(abc)\1没有匹配的后台
  作为交换,你得到高速正则表达式

答案 2 :(得分:1)

基于@OneOfOne's的答案,并改进了一些错误消息

package main

    import (
        "fmt"
        "strings"
        "unicode"
    )

    func verifyPassword(password string) error {
        var uppercasePresent bool
        var lowercasePresent bool
        var numberPresent bool
        var specialCharPresent bool
        const minPassLength = 8
        const maxPassLength = 64
        var passLen int
        var errorString string

        for _, ch := range password {
            switch {
            case unicode.IsNumber(ch):
                numberPresent = true
                passLen++
            case unicode.IsUpper(ch):
                uppercasePresent = true
                passLen++
            case unicode.IsLower(ch):
                lowercasePresent = true
                passLen++
            case unicode.IsPunct(ch) || unicode.IsSymbol(ch):
                specialCharPresent = true
                passLen++
            case ch == ' ':
                passLen++
            }
        }
        appendError := func(err string) {
            if len(strings.TrimSpace(errorString)) != 0 {
                errorString += ", " + err
            } else {
                errorString = err
            }
        }
        if !lowercasePresent {
            appendError("lowercase letter missing")
        }
        if !uppercasePresent {
            appendError("uppercase letter missing")
        }
        if !numberPresent {
            appendError("atleast one numeric character required")
        }
        if !specialCharPresent {
            appendError("special character missing")
        }
        if !(minPassLength <= passLen && passLen <= maxPassLength) {
            appendError(fmt.Sprintf("password length must be between %d to %d characters long", minPassLength, maxPassLength))
        }

        if len(errorString) != 0 {
            return fmt.Errorf(errorString)
        }
        return nil
    }

    // Let's test it
    func main() {
        password := "Apple"
        err := verifyPassword(password)
        fmt.Println(password, " ", err)
    }

答案 3 :(得分:0)

从邻近的答案开始,我也写了一个对我来说很好用的辅助函数。这只是假设总体密码长度令人满意。查看以下内容...

func isValid(s string) bool {
    var (
        hasMinLen  = false
        hasUpper   = false
        hasLower   = false
        hasNumber  = false
        hasSpecial = false
    )
    if len(s) >= 7 {
        hasMinLen = true
    }
    for _, char := range s {
        switch {
        case unicode.IsUpper(char):
            hasUpper = true
        case unicode.IsLower(char):
            hasLower = true
        case unicode.IsNumber(char):
            hasNumber = true
        case unicode.IsPunct(char) || unicode.IsSymbol(char):
            hasSpecial = true
        }
    }
    return hasMinLen && hasUpper && hasLower && hasNumber && hasSpecial
}

isValid("pass")     // false
isValid("password") // false
isValid("Password") // false
isValid("P@ssword") // false
isValid("P@ssw0rd") // true

答案 4 :(得分:0)

下面是我使用自定义消息实现上述答案,并以某种方式以一种好的方式扭曲它们(性能感知代码)。

package main

import (
    "fmt"
    "strconv"
    "unicode"
)

func main() {
    pass := "12345678_Windrol"

    // call the password validator and give it field name  to be known by the user, password, and the min and max password length
    isValid, errs := isValidPassword("Password", pass, 8, 32)
    if isValid {
        fmt.Println("The password is valid")
    } else {
        for _, v := range errs {
            fmt.Println(v)
        }
    }

}

func isValidPassword(field, s string, min, max int) (isValid bool, errs []string) {
    var (
        isMin   bool
        special bool
        number  bool
        upper   bool
        lower   bool
    )

    //test for the muximum and minimum characters required for the password string
    if len(s) < min || len(s) > max {
        isMin = false
        appendError("length should be " + strconv.Itoa(min) + " to " + strconv.Itoa(max))
    }

    for _, c := range s {
        // Optimize perf if all become true before reaching the end
        if special && number && upper && lower && isMin {
            break
        }

        // else go on switching
        switch {
        case unicode.IsUpper(c):
            upper = true
        case unicode.IsLower(c):
            lower = true
        case unicode.IsNumber(c):
            number = true
        case unicode.IsPunct(c) || unicode.IsSymbol(c):
            special = true
        }
    }

    // append error
    appendError := func(err string) {
        errs = append(errs, field+" "+err)
    }

    // Add custom error messages
    if !special {
        appendError("should contain at least a single special character")
    }
    if !number {
        appendError("should contain at least a single digit")
    }
    if !lower {
        appendError("should contain at least a single lowercase letter")
    }
    if !upper {
        appendError("should contain at least single uppercase letter")
    }

    // if there is any error
    if len(errs) > 0 {
        return false, errs
    }

    // everyting is right
    return true, errs
}