This commit is contained in:
@@ -24,6 +24,7 @@ version: '3.8'
|
|||||||
# - Gateway 服务配置(JUNHONG_GATEWAY_*)
|
# - Gateway 服务配置(JUNHONG_GATEWAY_*)
|
||||||
# - 对象存储配置(JUNHONG_STORAGE_*)
|
# - 对象存储配置(JUNHONG_STORAGE_*)
|
||||||
# - 短信服务配置(JUNHONG_SMS_*)
|
# - 短信服务配置(JUNHONG_SMS_*)
|
||||||
|
# - 客户端手机号绑定开关(JUNHONG_CLIENT_REQUIRE_PHONE_BINDING,默认 true)
|
||||||
#
|
#
|
||||||
# 微信公众号/小程序/支付配置已迁移至数据库(tb_wechat_config 表),
|
# 微信公众号/小程序/支付配置已迁移至数据库(tb_wechat_config 表),
|
||||||
# 不再需要环境变量和证书文件挂载。
|
# 不再需要环境变量和证书文件挂载。
|
||||||
@@ -50,6 +51,8 @@ services:
|
|||||||
- JUNHONG_REDIS_DB=6
|
- JUNHONG_REDIS_DB=6
|
||||||
# JWT 配置(必填)
|
# JWT 配置(必填)
|
||||||
- JUNHONG_JWT_SECRET_KEY=dev-secret-key-for-testing-only-32chars!
|
- JUNHONG_JWT_SECRET_KEY=dev-secret-key-for-testing-only-32chars!
|
||||||
|
# 客户端配置(可选,默认 true;false=不强制绑定手机号)
|
||||||
|
- JUNHONG_CLIENT_REQUIRE_PHONE_BINDING=false
|
||||||
# 日志配置
|
# 日志配置
|
||||||
- JUNHONG_LOGGING_LEVEL=info
|
- JUNHONG_LOGGING_LEVEL=info
|
||||||
- JUNHONG_LOGGING_DEVELOPMENT=false
|
- JUNHONG_LOGGING_DEVELOPMENT=false
|
||||||
@@ -107,6 +110,8 @@ services:
|
|||||||
- JUNHONG_REDIS_DB=6
|
- JUNHONG_REDIS_DB=6
|
||||||
# JWT 配置(必填)
|
# JWT 配置(必填)
|
||||||
- JUNHONG_JWT_SECRET_KEY=dev-secret-key-for-testing-only-32chars!
|
- JUNHONG_JWT_SECRET_KEY=dev-secret-key-for-testing-only-32chars!
|
||||||
|
# 客户端配置(可选,默认 true;false=不强制绑定手机号)
|
||||||
|
- JUNHONG_CLIENT_REQUIRE_PHONE_BINDING=false
|
||||||
# 日志配置
|
# 日志配置
|
||||||
- JUNHONG_LOGGING_LEVEL=info
|
- JUNHONG_LOGGING_LEVEL=info
|
||||||
- JUNHONG_LOGGING_DEVELOPMENT=false
|
- JUNHONG_LOGGING_DEVELOPMENT=false
|
||||||
|
|||||||
@@ -124,6 +124,12 @@
|
|||||||
| `JUNHONG_JWT_ACCESS_TOKEN_TTL` | `24h` | Access Token TTL |
|
| `JUNHONG_JWT_ACCESS_TOKEN_TTL` | `24h` | Access Token TTL |
|
||||||
| `JUNHONG_JWT_REFRESH_TOKEN_TTL` | `168h` | Refresh Token TTL (7天) |
|
| `JUNHONG_JWT_REFRESH_TOKEN_TTL` | `168h` | Refresh Token TTL (7天) |
|
||||||
|
|
||||||
|
### 客户端配置
|
||||||
|
|
||||||
|
| 环境变量 | 默认值 | 说明 |
|
||||||
|
|---------|--------|------|
|
||||||
|
| `JUNHONG_CLIENT_REQUIRE_PHONE_BINDING` | `true` | 是否强制 C 端用户绑定手机号(`true` 强制,`false` 不强制) |
|
||||||
|
|
||||||
### 队列配置
|
### 队列配置
|
||||||
|
|
||||||
| 环境变量 | 默认值 | 说明 |
|
| 环境变量 | 默认值 | 说明 |
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import (
|
|||||||
wechatConfigSvc "github.com/break/junhong_cmp_fiber/internal/service/wechat_config"
|
wechatConfigSvc "github.com/break/junhong_cmp_fiber/internal/service/wechat_config"
|
||||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/auth"
|
"github.com/break/junhong_cmp_fiber/pkg/auth"
|
||||||
|
"github.com/break/junhong_cmp_fiber/pkg/config"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/wechat"
|
"github.com/break/junhong_cmp_fiber/pkg/wechat"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -439,19 +439,27 @@ func (s *Service) signAssetToken(assetType string, assetID uint) (string, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
return token.SignedString([]byte(viper.GetString("jwt.secret_key") + ":asset"))
|
cfg := config.Get()
|
||||||
|
if cfg == nil || cfg.JWT.SecretKey == "" {
|
||||||
|
return "", errors.New(errors.CodeInternalError, "JWT 密钥未配置")
|
||||||
|
}
|
||||||
|
return token.SignedString([]byte(cfg.JWT.SecretKey + ":asset"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) verifyAssetToken(assetToken string) (*assetTokenClaims, error) {
|
func (s *Service) verifyAssetToken(assetToken string) (*assetTokenClaims, error) {
|
||||||
if assetToken == "" {
|
if assetToken == "" {
|
||||||
return nil, errors.New(errors.CodeInvalidParam)
|
return nil, errors.New(errors.CodeInvalidParam)
|
||||||
}
|
}
|
||||||
|
cfg := config.Get()
|
||||||
|
if cfg == nil || cfg.JWT.SecretKey == "" {
|
||||||
|
return nil, errors.New(errors.CodeInternalError, "JWT 密钥未配置")
|
||||||
|
}
|
||||||
|
|
||||||
parsed, err := jwt.ParseWithClaims(assetToken, &assetTokenClaims{}, func(token *jwt.Token) (any, error) {
|
parsed, err := jwt.ParseWithClaims(assetToken, &assetTokenClaims{}, func(token *jwt.Token) (any, error) {
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
return nil, errors.New(errors.CodeInvalidToken)
|
return nil, errors.New(errors.CodeInvalidToken)
|
||||||
}
|
}
|
||||||
return []byte(viper.GetString("jwt.secret_key") + ":asset"), nil
|
return []byte(cfg.JWT.SecretKey + ":asset"), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New(errors.CodeInvalidToken)
|
return nil, errors.New(errors.CodeInvalidToken)
|
||||||
@@ -719,12 +727,17 @@ func (s *Service) issueLoginToken(ctx context.Context, customerID uint, assetTyp
|
|||||||
// 查询用户已绑定的主手机号,写入 JWT,供后续接口直接从 context 取用
|
// 查询用户已绑定的主手机号,写入 JWT,供后续接口直接从 context 取用
|
||||||
var boundPhone string
|
var boundPhone string
|
||||||
needBindPhone := false
|
needBindPhone := false
|
||||||
|
cfg := config.Get()
|
||||||
|
requirePhoneBinding := true
|
||||||
|
if cfg != nil {
|
||||||
|
requirePhoneBinding = cfg.Client.RequirePhoneBinding
|
||||||
|
}
|
||||||
primaryPhone, phoneErr := s.phoneStore.GetPrimaryPhone(ctx, customerID)
|
primaryPhone, phoneErr := s.phoneStore.GetPrimaryPhone(ctx, customerID)
|
||||||
if phoneErr == nil {
|
if phoneErr == nil {
|
||||||
boundPhone = primaryPhone.Phone
|
boundPhone = primaryPhone.Phone
|
||||||
} else if phoneErr != gorm.ErrRecordNotFound {
|
} else if phoneErr != gorm.ErrRecordNotFound {
|
||||||
return "", false, errors.Wrap(errors.CodeInternalError, phoneErr, "查询手机号绑定关系失败")
|
return "", false, errors.Wrap(errors.CodeInternalError, phoneErr, "查询手机号绑定关系失败")
|
||||||
} else if viper.GetBool("client.require_phone_binding") {
|
} else if requirePhoneBinding {
|
||||||
needBindPhone = true
|
needBindPhone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type Config struct {
|
|||||||
Queue QueueConfig `mapstructure:"queue"`
|
Queue QueueConfig `mapstructure:"queue"`
|
||||||
Logging LoggingConfig `mapstructure:"logging"`
|
Logging LoggingConfig `mapstructure:"logging"`
|
||||||
Middleware MiddlewareConfig `mapstructure:"middleware"`
|
Middleware MiddlewareConfig `mapstructure:"middleware"`
|
||||||
|
Client ClientConfig `mapstructure:"client"`
|
||||||
SMS SMSConfig `mapstructure:"sms"`
|
SMS SMSConfig `mapstructure:"sms"`
|
||||||
JWT JWTConfig `mapstructure:"jwt"`
|
JWT JWTConfig `mapstructure:"jwt"`
|
||||||
DefaultAdmin DefaultAdminConfig `mapstructure:"default_admin"`
|
DefaultAdmin DefaultAdminConfig `mapstructure:"default_admin"`
|
||||||
@@ -94,6 +95,11 @@ type MiddlewareConfig struct {
|
|||||||
RateLimiter RateLimiterConfig `mapstructure:"rate_limiter"` // 限流器配置
|
RateLimiter RateLimiterConfig `mapstructure:"rate_limiter"` // 限流器配置
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientConfig 客户端行为开关配置
|
||||||
|
type ClientConfig struct {
|
||||||
|
RequirePhoneBinding bool `mapstructure:"require_phone_binding"` // 是否要求个人客户绑定手机号
|
||||||
|
}
|
||||||
|
|
||||||
// RateLimiterConfig 限流器配置
|
// RateLimiterConfig 限流器配置
|
||||||
type RateLimiterConfig struct {
|
type RateLimiterConfig struct {
|
||||||
Max int `mapstructure:"max"` // 时间窗口内的最大请求数
|
Max int `mapstructure:"max"` // 时间窗口内的最大请求数
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ func bindEnvVariables(v *viper.Viper) {
|
|||||||
"jwt.token_duration",
|
"jwt.token_duration",
|
||||||
"jwt.access_token_ttl",
|
"jwt.access_token_ttl",
|
||||||
"jwt.refresh_token_ttl",
|
"jwt.refresh_token_ttl",
|
||||||
|
"client.require_phone_binding",
|
||||||
"middleware.enable_rate_limiter",
|
"middleware.enable_rate_limiter",
|
||||||
"middleware.rate_limiter.max",
|
"middleware.rate_limiter.max",
|
||||||
"middleware.rate_limiter.expiration",
|
"middleware.rate_limiter.expiration",
|
||||||
|
|||||||
@@ -273,6 +273,12 @@ export JUNHONG_REDIS_DB="$REDIS_DB"
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
export JUNHONG_JWT_SECRET_KEY="$JWT_SECRET_KEY"
|
export JUNHONG_JWT_SECRET_KEY="$JWT_SECRET_KEY"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# 客户端配置(可选)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# 是否强制 C 端用户绑定手机号(true: 强制,false: 不强制)
|
||||||
|
export JUNHONG_CLIENT_REQUIRE_PHONE_BINDING="true"
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# 服务器配置
|
# 服务器配置
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user