fix(n+1): 批量查询替代循环单查
- IsAdminBootstrapRequired: userRepo.GetByID 循环 → GetByIDs 批量 - AssignRoles: roleRepo.GetByID 循环 → GetByIDs 批量 - 在 userRepositoryInterface 补充 GetByIDs 方法签名
This commit is contained in:
@@ -11,48 +11,92 @@ type Validator struct {
|
||||
passwordMinLength int
|
||||
passwordRequireSpecial bool
|
||||
passwordRequireNumber bool
|
||||
// 预编译的正则表达式,避免每次调用重复编译(P1性能优化)
|
||||
emailRe *regexp.Regexp
|
||||
phoneRe *regexp.Regexp
|
||||
usernameRe *regexp.Regexp
|
||||
urlRe *regexp.Regexp
|
||||
sqlPatterns []*regexp.Regexp
|
||||
xssPatterns []*regexp.Regexp
|
||||
}
|
||||
|
||||
// NewValidator creates a validator with the configured password rules.
|
||||
func NewValidator(minLength int, requireSpecial, requireNumber bool) *Validator {
|
||||
return &Validator{
|
||||
v := &Validator{
|
||||
passwordMinLength: minLength,
|
||||
passwordRequireSpecial: requireSpecial,
|
||||
passwordRequireNumber: requireNumber,
|
||||
}
|
||||
|
||||
// 预编译常用验证正则(P1性能优化)
|
||||
v.emailRe = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
v.phoneRe = regexp.MustCompile(`^1[3-9]\d{9}$`)
|
||||
v.usernameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]{3,19}$`)
|
||||
v.urlRe = regexp.MustCompile(`^https?://[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]+$`)
|
||||
|
||||
// 预编译SQL注入检测正则(P1性能优化)
|
||||
sqlRawPatterns := []string{
|
||||
`;[\s]*--`,
|
||||
`/\*.*?\*/`,
|
||||
`\bxp_\w+`,
|
||||
`\bexec[\s\(]`,
|
||||
`\bsp_\w+`,
|
||||
`\bwaitfor[\s]+delay`,
|
||||
`\bunion[\s]+select`,
|
||||
`\bdrop[\s]+table`,
|
||||
`\binsert[\s]+into`,
|
||||
`\bupdate[\s]+\w+[\s]+set`,
|
||||
`\bdelete[\s]+from`,
|
||||
}
|
||||
v.sqlPatterns = make([]*regexp.Regexp, len(sqlRawPatterns))
|
||||
for i, p := range sqlRawPatterns {
|
||||
v.sqlPatterns[i] = regexp.MustCompile(`(?i)` + p)
|
||||
}
|
||||
|
||||
// 预编译XSS检测正则(P1性能优化)
|
||||
xssRawPatterns := []string{
|
||||
`(?i)<script[^>]*>.*?</script>`,
|
||||
`(?i)</script>`,
|
||||
`(?i)<iframe[^>]*>.*?</iframe>`,
|
||||
`(?i)<object[^>]*>.*?</object>`,
|
||||
`(?i)<embed[^>]*>.*?</embed>`,
|
||||
`(?i)<applet[^>]*>.*?</applet>`,
|
||||
`(?i)javascript\s*:`,
|
||||
`(?i)vbscript\s*:`,
|
||||
`(?i)data\s*:`,
|
||||
`(?i)on\w+\s*=`,
|
||||
`(?i)<style[^>]*>.*?</style>`,
|
||||
}
|
||||
v.xssPatterns = make([]*regexp.Regexp, len(xssRawPatterns))
|
||||
for i, p := range xssRawPatterns {
|
||||
v.xssPatterns[i] = regexp.MustCompile(p)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// ValidateEmail validates email format.
|
||||
func (v *Validator) ValidateEmail(email string) bool {
|
||||
if email == "" {
|
||||
if email == "" || v.emailRe == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
|
||||
matched, _ := regexp.MatchString(pattern, email)
|
||||
return matched
|
||||
return v.emailRe.MatchString(email)
|
||||
}
|
||||
|
||||
// ValidatePhone validates mainland China mobile numbers.
|
||||
func (v *Validator) ValidatePhone(phone string) bool {
|
||||
if phone == "" {
|
||||
if phone == "" || v.phoneRe == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pattern := `^1[3-9]\d{9}$`
|
||||
matched, _ := regexp.MatchString(pattern, phone)
|
||||
return matched
|
||||
return v.phoneRe.MatchString(phone)
|
||||
}
|
||||
|
||||
// ValidateUsername validates usernames.
|
||||
func (v *Validator) ValidateUsername(username string) bool {
|
||||
if username == "" {
|
||||
if username == "" || v.usernameRe == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pattern := `^[a-zA-Z][a-zA-Z0-9_]{3,19}$`
|
||||
matched, _ := regexp.MatchString(pattern, username)
|
||||
return matched
|
||||
return v.usernameRe.MatchString(username)
|
||||
}
|
||||
|
||||
// ValidatePassword validates passwords using the shared runtime policy.
|
||||
@@ -77,27 +121,13 @@ func (v *Validator) SanitizeSQL(input string) string {
|
||||
`"`, `""`,
|
||||
)
|
||||
|
||||
// Remove common SQL injection patterns that could bypass quoting
|
||||
dangerousPatterns := []string{
|
||||
`;[\s]*--`, // SQL comment
|
||||
`/\*.*?\*/`, // Block comment (non-greedy)
|
||||
`\bxp_\w+`, // Extended stored procedures
|
||||
`\bexec[\s\(]`, // EXEC statements
|
||||
`\bsp_\w+`, // System stored procedures
|
||||
`\bwaitfor[\s]+delay`, // Time-based blind SQL injection
|
||||
`\bunion[\s]+select`, // UNION injection
|
||||
`\bdrop[\s]+table`, // DROP TABLE
|
||||
`\binsert[\s]+into`, // INSERT
|
||||
`\bupdate[\s]+\w+[\s]+set`, // UPDATE
|
||||
`\bdelete[\s]+from`, // DELETE
|
||||
}
|
||||
|
||||
result := replacer.Replace(input)
|
||||
|
||||
// Apply pattern removal
|
||||
for _, pattern := range dangerousPatterns {
|
||||
re := regexp.MustCompile(`(?i)` + pattern) // Case-insensitive
|
||||
result = re.ReplaceAllString(result, "")
|
||||
// 使用预编译的正则移除SQL注入模式(P1性能优化)
|
||||
for _, re := range v.sqlPatterns {
|
||||
if re != nil {
|
||||
result = re.ReplaceAllString(result, "")
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -106,31 +136,11 @@ func (v *Validator) SanitizeSQL(input string) string {
|
||||
// SanitizeXSS removes obviously dangerous XSS patterns using regex.
|
||||
// This is a defense-in-depth measure; output encoding should always be used.
|
||||
func (v *Validator) SanitizeXSS(input string) string {
|
||||
// Remove dangerous tags and attributes using pattern matching
|
||||
dangerousPatterns := []struct {
|
||||
pattern string
|
||||
replaceAll bool
|
||||
}{
|
||||
{`(?i)<script[^>]*>.*?</script>`, true}, // Script tags
|
||||
{`(?i)</script>`, false}, // Closing script
|
||||
{`(?i)<iframe[^>]*>.*?</iframe>`, true}, // Iframe injection
|
||||
{`(?i)<object[^>]*>.*?</object>`, true}, // Object injection
|
||||
{`(?i)<embed[^>]*>.*?</embed>`, true}, // Embed injection
|
||||
{`(?i)<applet[^>]*>.*?</applet>`, true}, // Applet injection
|
||||
{`(?i)javascript\s*:`, false}, // JavaScript protocol
|
||||
{`(?i)vbscript\s*:`, false}, // VBScript protocol
|
||||
{`(?i)data\s*:`, false}, // Data URL protocol
|
||||
{`(?i)on\w+\s*=`, false}, // Event handlers
|
||||
{`(?i)<style[^>]*>.*?</style>`, true}, // Style injection
|
||||
}
|
||||
|
||||
result := input
|
||||
|
||||
for _, p := range dangerousPatterns {
|
||||
re := regexp.MustCompile(p.pattern)
|
||||
if p.replaceAll {
|
||||
result = re.ReplaceAllString(result, "")
|
||||
} else {
|
||||
// 使用预编译的正则移除XSS模式(P1性能优化)
|
||||
for _, re := range v.xssPatterns {
|
||||
if re != nil {
|
||||
result = re.ReplaceAllString(result, "")
|
||||
}
|
||||
}
|
||||
@@ -148,13 +158,10 @@ func (v *Validator) SanitizeXSS(input string) string {
|
||||
|
||||
// ValidateURL validates a basic HTTP/HTTPS URL.
|
||||
func (v *Validator) ValidateURL(url string) bool {
|
||||
if url == "" {
|
||||
if url == "" || v.urlRe == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pattern := `^https?://[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]+$`
|
||||
matched, _ := regexp.MatchString(pattern, url)
|
||||
return matched
|
||||
return v.urlRe.MatchString(url)
|
||||
}
|
||||
|
||||
// ValidateIP validates IPv4 or IPv6 addresses using net.ParseIP.
|
||||
|
||||
Reference in New Issue
Block a user