package svc import ( "perms-system-server/internal/config" "perms-system-server/internal/loaders" "perms-system-server/internal/middleware" "perms-system-server/internal/model" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/zeromicro/go-zero/core/limit" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/rest" ) type ServiceContext struct { Config config.Config JwtAuth rest.Middleware ProductLoginRateLimit rest.Middleware AdminLoginRateLimit rest.Middleware SyncRateLimit rest.Middleware RefreshTokenRateLimit rest.Middleware GrpcLoginLimiter *limit.PeriodLimit GrpcRefreshLimiter *limit.PeriodLimit GrpcVerifyLimiter *limit.PeriodLimit GrpcSyncLimiter *limit.PeriodLimit GrpcGetUserPermsLimiter *limit.PeriodLimit UsernameLoginLimit *limit.PeriodLimit TokenOpLimiter *limit.PeriodLimit UserDetailsLoader *loaders.UserDetailsLoader Redis *redis.Redis MinioClient *minio.Client *model.Models } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.MySQL.DataSource) rds := redis.MustNewRedis(c.CacheRedis.Nodes[0].RedisConf) models := model.NewModels(conn, c.CacheRedis.Nodes, c.CacheRedis.KeyPrefix) udLoader := loaders.NewUserDetailsLoader(rds, c.CacheRedis.KeyPrefix, models) productLoginRL := middleware.NewProductLoginRateLimitMiddleware(rds, c.CacheRedis.KeyPrefix, c.BehindProxy) adminLoginRL := middleware.NewAdminLoginRateLimitMiddleware(rds, c.CacheRedis.KeyPrefix, c.BehindProxy) syncRlMiddleware := middleware.NewSyncRateLimitMiddleware(rds, c.CacheRedis.KeyPrefix, c.BehindProxy) refreshTokenRL := middleware.NewRefreshTokenRateLimitMiddleware(rds, c.CacheRedis.KeyPrefix, c.BehindProxy) grpcLimiter := limit.NewPeriodLimit(60, 20, rds, c.CacheRedis.KeyPrefix+":rl:grpc:login") // gRPC refreshToken 一般低频操作(分钟级),限紧一点可以同时防签名爆破与并发刷新被用作会话劫持的放大器。 grpcRefreshLimiter := limit.NewPeriodLimit(60, 30, rds, c.CacheRedis.KeyPrefix+":rl:grpc:refresh") // gRPC verifyToken 是下游每请求都会调用的热路径,阈值必须足够高;这里的作用是兜底防止下游被攻破后把权限中心当 token oracle 爆破。 grpcVerifyLimiter := limit.NewPeriodLimit(60, 6000, rds, c.CacheRedis.KeyPrefix+":rl:grpc:verify") // 审计 M-R11-1:gRPC SyncPermissions / GetUserPerms 原来没有入口限流,而 HTTP 侧 /api/perm/sync // 已经挂 SyncRateLimit。限流 key 走 appKey 维度,避免按 IP 把"同一产品不同后端实例共享 egress" // 整组误伤。桶位按单产品实际节奏给出:单产品每分钟 60 次同步足以覆盖多实例并发发版的真实用量, // GetUserPerms 1000 次/分钟/产品覆盖多实例冷启动预热峰值,而 appSecret 泄露时能把放大系数压回 // 可控量级。 grpcSyncLimiter := limit.NewPeriodLimit(60, 60, rds, c.CacheRedis.KeyPrefix+":rl:grpc:sync") grpcGetUserPermsLimiter := limit.NewPeriodLimit(60, 1000, rds, c.CacheRedis.KeyPrefix+":rl:grpc:perms") usernameLimiter := limit.NewPeriodLimit(300, 10, rds, c.CacheRedis.KeyPrefix+":rl:user") tokenOpLimiter := limit.NewPeriodLimit(60, 10, rds, c.CacheRedis.KeyPrefix+":rl:tokenop") var minioClient *minio.Client if c.Minio.Endpoint != "" { var err error minioClient, err = minio.New(c.Minio.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4(c.Minio.AccessKeyId, c.Minio.AccessKeySecret, ""), Secure: c.Minio.UseSSL, }) if err != nil { logx.Must(err) } } return &ServiceContext{ Config: c, JwtAuth: middleware.NewJwtAuthMiddleware(c.Auth.AccessSecret, udLoader).Handle, ProductLoginRateLimit: productLoginRL.Handle, AdminLoginRateLimit: adminLoginRL.Handle, SyncRateLimit: syncRlMiddleware.Handle, RefreshTokenRateLimit: refreshTokenRL.Handle, GrpcLoginLimiter: grpcLimiter, GrpcRefreshLimiter: grpcRefreshLimiter, GrpcVerifyLimiter: grpcVerifyLimiter, GrpcSyncLimiter: grpcSyncLimiter, GrpcGetUserPermsLimiter: grpcGetUserPermsLimiter, UsernameLoginLimit: usernameLimiter, TokenOpLimiter: tokenOpLimiter, UserDetailsLoader: udLoader, Redis: rds, MinioClient: minioClient, Models: models, } }