feat(config): add sensitive word filtering system with database support
Implement sensitive word filtering feature with configurable database and Redis support. Add security enhancements including WebSocket origin validation, improved password strength requirements, timing attack prevention, and production JWT secret validation. Add batch operation validation limits and optimize user blocking queries with subqueries. BREAKING CHANGE: Password validation now requires minimum 8 characters with uppercase, lowercase, and digit (was 6-50 characters without complexity requirements)
This commit is contained in:
@@ -12,9 +12,9 @@ import (
|
||||
|
||||
"carrot_bbs/internal/model"
|
||||
redisclient "carrot_bbs/internal/pkg/redis"
|
||||
"carrot_bbs/internal/repository"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ==================== DFA 敏感词过滤实现 ====================
|
||||
@@ -250,7 +250,7 @@ type SensitiveService interface {
|
||||
// sensitiveServiceImpl 敏感词服务实现
|
||||
type sensitiveServiceImpl struct {
|
||||
tree *SensitiveWordTree
|
||||
db *gorm.DB
|
||||
repo repository.SensitiveWordRepository
|
||||
redis *redisclient.Client
|
||||
config *SensitiveConfig
|
||||
mu sync.RWMutex
|
||||
@@ -272,10 +272,10 @@ type SensitiveConfig struct {
|
||||
}
|
||||
|
||||
// NewSensitiveService 创建敏感词服务
|
||||
func NewSensitiveService(db *gorm.DB, redisClient *redisclient.Client, config *SensitiveConfig) SensitiveService {
|
||||
func NewSensitiveService(repo repository.SensitiveWordRepository, redisClient *redisclient.Client, config *SensitiveConfig) SensitiveService {
|
||||
s := &sensitiveServiceImpl{
|
||||
tree: NewSensitiveWordTree(),
|
||||
db: db,
|
||||
repo: repo,
|
||||
redis: redisClient,
|
||||
config: config,
|
||||
replaceStr: config.ReplaceStr,
|
||||
@@ -356,33 +356,16 @@ func (s *sensitiveServiceImpl) AddWord(ctx context.Context, word string, categor
|
||||
s.tree.AddWord(word, wordLevel, wordCategory)
|
||||
|
||||
// 持久化到数据库
|
||||
if s.db != nil {
|
||||
sensitiveWord := model.SensitiveWord{
|
||||
if s.repo != nil {
|
||||
sensitiveWord := &model.SensitiveWord{
|
||||
Word: word,
|
||||
Category: wordCategory,
|
||||
Level: wordLevel,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
// 使用 upsert 逻辑
|
||||
var existing model.SensitiveWord
|
||||
result := s.db.Where("word = ?", word).First(&existing)
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
if err := s.db.Create(&sensitiveWord).Error; err != nil {
|
||||
zap.L().Warn("Failed to save sensitive word to database",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
} else if result.Error == nil {
|
||||
// 更新已存在的记录
|
||||
existing.Category = wordCategory
|
||||
existing.Level = wordLevel
|
||||
existing.IsActive = true
|
||||
if err := s.db.Save(&existing).Error; err != nil {
|
||||
zap.L().Warn("Failed to update sensitive word in database",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
if err := s.repo.Upsert(ctx, sensitiveWord); err != nil {
|
||||
return fmt.Errorf("failed to upsert sensitive word to database: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,12 +394,9 @@ func (s *sensitiveServiceImpl) RemoveWord(ctx context.Context, word string) erro
|
||||
s.tree.RemoveWord(word)
|
||||
|
||||
// 从数据库中标记为不活跃
|
||||
if s.db != nil {
|
||||
result := s.db.Model(&model.SensitiveWord{}).Where("word = ?", word).Update("is_active", false)
|
||||
if result.Error != nil {
|
||||
zap.L().Warn("Failed to deactivate sensitive word in database",
|
||||
zap.Error(result.Error),
|
||||
)
|
||||
if s.repo != nil {
|
||||
if err := s.repo.DeactivateByWord(ctx, word); err != nil {
|
||||
return fmt.Errorf("failed to deactivate sensitive word in database: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,12 +438,12 @@ func (s *sensitiveServiceImpl) GetWordCount(ctx context.Context) int {
|
||||
|
||||
// loadFromDB 从数据库加载敏感词
|
||||
func (s *sensitiveServiceImpl) loadFromDB(ctx context.Context) error {
|
||||
if s.db == nil {
|
||||
if s.repo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var words []model.SensitiveWord
|
||||
if err := s.db.Where("is_active = ?", true).Find(&words).Error; err != nil {
|
||||
words, err := s.repo.ListActive(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user