fix(n+1): 批量查询替代循环单查

- IsAdminBootstrapRequired: userRepo.GetByID 循环 → GetByIDs 批量
- AssignRoles: roleRepo.GetByID 循环 → GetByIDs 批量
- 在 userRepositoryInterface 补充 GetByIDs 方法签名
This commit is contained in:
2026-05-08 08:05:26 +08:00
parent 9b1cea246e
commit 2a18a6fb47
39 changed files with 3169 additions and 393 deletions

View File

@@ -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.