<feat>api表单验证
This commit is contained in:
		
							parent
							
								
									8ead5e11e2
								
							
						
					
					
						commit
						b1a1a4cd50
					
				|  | @ -30,21 +30,22 @@ const ( | ||||||
| 
 | 
 | ||||||
| 	// app
 | 	// app
 | ||||||
| 
 | 
 | ||||||
| 	AppNotFound              = 1200 | 	AppNotFound               = 1200 | ||||||
| 	AppDisabled              = 1201 | 	AppDisabled               = 1201 | ||||||
| 	AppIpNotAllow            = 1202 | 	AppIpNotAllow             = 1202 | ||||||
| 	AppRsaDecryptKeyNotFound = 1203 | 	AppRsaDecryptKeyNotFound  = 1203 | ||||||
| 	AppRsaDecryptFail        = 1210 | 	AppDecryptDataDiscrepancy = 1204 | ||||||
| 	AppRsaEncryptKeyNotFound = 1211 | 	AppRsaDecryptFail         = 1210 | ||||||
| 	AppRsaEncryptFail        = 1212 | 	AppRsaEncryptKeyNotFound  = 1211 | ||||||
| 	AppSM2DecryptKeyNotFound = 1220 | 	AppRsaEncryptFail         = 1212 | ||||||
| 	AppSM2DecryptFail        = 1221 | 	AppSM2DecryptKeyNotFound  = 1220 | ||||||
| 	AppSM2EncryptKeyNotFound = 1222 | 	AppSM2DecryptFail         = 1221 | ||||||
| 	AppSM2EncryptFail        = 1223 | 	AppSM2EncryptKeyNotFound  = 1222 | ||||||
| 	AppSM4DecryptKeyNotFound = 1230 | 	AppSM2EncryptFail         = 1223 | ||||||
| 	AppSM4DecryptFail        = 1231 | 	AppSM4DecryptKeyNotFound  = 1230 | ||||||
| 	AppSM4EncryptKeyNotFound = 1232 | 	AppSM4DecryptFail         = 1231 | ||||||
| 	AppSM4EncryptFail        = 1233 | 	AppSM4EncryptKeyNotFound  = 1232 | ||||||
|  | 	AppSM4EncryptFail         = 1233 | ||||||
| 
 | 
 | ||||||
| 	//渠道
 | 	//渠道
 | ||||||
| 	PayChannelNotFound = 1300 | 	PayChannelNotFound = 1300 | ||||||
|  | @ -60,16 +61,17 @@ var MsgEN = map[int]string{ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var MsgZH = map[int]string{ | var MsgZH = map[int]string{ | ||||||
| 	Success:          "请求成功", | 	Success:                   "请求成功", | ||||||
| 	ParamError:       "参数错误", | 	ParamError:                "参数错误", | ||||||
| 	NotFound:         "数据不存在", | 	NotFound:                  "数据不存在", | ||||||
| 	NotAuth:          "未经授权", | 	NotAuth:                   "未经授权", | ||||||
| 	NotLogin:         "未登录", | 	NotLogin:                  "未登录", | ||||||
| 	RequestTimeOut:   "请求超时", | 	RequestTimeOut:            "请求超时", | ||||||
| 	MerchantNotFound: "商户不存在", | 	MerchantNotFound:          "商户不存在", | ||||||
| 	AppNotFound:      "app_id未找到", | 	AppNotFound:               "app_id未找到", | ||||||
| 	AppDisabled:      "app通道关闭", | 	AppDisabled:               "app通道关闭", | ||||||
| 	AppIpNotAllow:    "ip不在白名单内", | 	AppIpNotAllow:             "ip不在白名单内", | ||||||
|  | 	AppDecryptDataDiscrepancy: "解密数据不一致", | ||||||
| 
 | 
 | ||||||
| 	AppRsaDecryptKeyNotFound: "密匙缺失,无法进行Rsa解密", | 	AppRsaDecryptKeyNotFound: "密匙缺失,无法进行Rsa解密", | ||||||
| 	AppRsaDecryptFail:        "Rsa解密失败", | 	AppRsaDecryptFail:        "Rsa解密失败", | ||||||
|  |  | ||||||
|  | @ -149,6 +149,34 @@ func GenRequest(c *gin.Context, request interface{}) (msgs []string, err error) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func ValidApiData(dataByte []byte, validStruct interface{}) (msgs []string, err error) { | ||||||
|  | 	validate := validator.New() | ||||||
|  | 	err = json.Unmarshal(dataByte, validStruct) | ||||||
|  | 	if err != nil { | ||||||
|  | 		err = errors.New(errorcode.GetMsg(errorcode.ParamError, "")) | ||||||
|  | 	} | ||||||
|  | 	zh_ch := zh.New() | ||||||
|  | 	uni := ut.New(zh_ch) | ||||||
|  | 	trans, _ := uni.GetTranslator("zh") | ||||||
|  | 	//注册一个函数,获取struct tag里自定义的label作为字段名
 | ||||||
|  | 	validate.RegisterTagNameFunc(func(fld reflect.StructField) string { | ||||||
|  | 		name := fld.Tag.Get("label") | ||||||
|  | 		return name | ||||||
|  | 	}) | ||||||
|  | 	//验证器注册翻译器
 | ||||||
|  | 	_ = zh_translations.RegisterDefaultTranslations(validate, trans) | ||||||
|  | 	errValidate := validate.Struct(validStruct) | ||||||
|  | 	if errValidate != nil { | ||||||
|  | 		// 如果有错误,打印出来
 | ||||||
|  | 		for _, v := range errValidate.(validator.ValidationErrors) { | ||||||
|  | 			msgs = append(msgs, v.Translate(trans)) | ||||||
|  | 		} | ||||||
|  | 		err = errors.New(errorcode.GetMsg(errorcode.ParamError, "")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // 重复读取body
 | // 重复读取body
 | ||||||
| func ReadBody(c *gin.Context) (body []byte, err error) { | func ReadBody(c *gin.Context) (body []byte, err error) { | ||||||
| 	body, err = ioutil.ReadAll(c.Request.Body) | 	body, err = ioutil.ReadAll(c.Request.Body) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | package front | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func WebPay(c *gin.Context) { | ||||||
|  | 	fmt.Println("312312312321") | ||||||
|  | } | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| package front | package front | ||||||
| 
 | 
 | ||||||
| type PayCommonBody struct { | type ApiCommonBody struct { | ||||||
| 	AppId     int64 `json:"app_id" validate:"required"` | 	AppId     int64 `json:"app_id" validate:"required"` | ||||||
| 	Timestamp int64 `json:"timestamp" validate:"required"` | 	Timestamp int64 `json:"timestamp" validate:"required"` | ||||||
| } | } | ||||||
|  | @ -12,7 +12,7 @@ type RequestBody struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type PayWeb struct { | type PayWeb struct { | ||||||
| 	PayCommonBody | 	ApiCommonBody | ||||||
| 	PayChannel int64 `json:"private_key_path"` | 	PayChannel int64 `json:"private_key_path"  validate:"required" label:"支付渠道"` | ||||||
| 	Delay      int64 `json:"delay"` //延时时间
 | 	Delay      int64 `json:"delay"` //延时时间
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import ( | ||||||
| 	"PaymentCenter/app/http/entities/front" | 	"PaymentCenter/app/http/entities/front" | ||||||
| 	"PaymentCenter/app/http/requestmapping" | 	"PaymentCenter/app/http/requestmapping" | ||||||
| 	"PaymentCenter/app/services" | 	"PaymentCenter/app/services" | ||||||
|  | 	"PaymentCenter/app/services/apicrypt" | ||||||
| 	"PaymentCenter/app/utils" | 	"PaymentCenter/app/utils" | ||||||
| 	"PaymentCenter/config" | 	"PaymentCenter/config" | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | @ -107,7 +108,7 @@ func ValidateRequest() gin.HandlerFunc { | ||||||
| 		if strings.Index(path, "admin") >= 0 { | 		if strings.Index(path, "admin") >= 0 { | ||||||
| 			handler = requestmapping.BackendRequestMap[path] | 			handler = requestmapping.BackendRequestMap[path] | ||||||
| 		} else { | 		} else { | ||||||
| 			handler = requestmapping.FrontRequestMap[path] | 			handler = requestmapping.FrontRequest() | ||||||
| 		} | 		} | ||||||
| 		if handler == nil { | 		if handler == nil { | ||||||
| 			utils.Log(c, "path", path) | 			utils.Log(c, "path", path) | ||||||
|  | @ -129,11 +130,14 @@ func ValidateRequest() gin.HandlerFunc { | ||||||
| 
 | 
 | ||||||
| func ValidatePayRequest() gin.HandlerFunc { | func ValidatePayRequest() gin.HandlerFunc { | ||||||
| 	return func(c *gin.Context) { | 	return func(c *gin.Context) { | ||||||
| 		commonData, err := utils.SonicApiDataToStruct(controllers.GetRequest(c), &front.RequestBody{}) | 		var path = c.FullPath() | ||||||
|  | 		var handler func() interface{} | ||||||
|  | 		requestData, err := utils.SonicApiDataToStruct(controllers.GetRequest(c), &front.RequestBody{}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			controllers.ErrWithCode(c, errorcode.ParamError) | 			controllers.ErrWithCode(c, errorcode.ParamError) | ||||||
|  | 			return | ||||||
| 		} | 		} | ||||||
| 		commonDataStruct := commonData.(*front.RequestBody) | 		requestDataStruct := requestData.(*front.RequestBody) | ||||||
| 		//判断时间
 | 		//判断时间
 | ||||||
| 		//now := time.Now().UnixNano() / 1000000
 | 		//now := time.Now().UnixNano() / 1000000
 | ||||||
| 		//if comStruct.Timestamp > now || (config.GetConf().TimeOut != 0 && (now-comStruct.Timestamp) > config.GetConf().TimeOut) {
 | 		//if comStruct.Timestamp > now || (config.GetConf().TimeOut != 0 && (now-comStruct.Timestamp) > config.GetConf().TimeOut) {
 | ||||||
|  | @ -141,7 +145,7 @@ func ValidatePayRequest() gin.HandlerFunc { | ||||||
| 		//	return
 | 		//	return
 | ||||||
| 		//}
 | 		//}
 | ||||||
| 		//获取app信息
 | 		//获取app信息
 | ||||||
| 		app, errCode := services.AppFindOne(entities.IdRequest{Id: commonDataStruct.AppId}) | 		app, errCode := services.AppFindOne(entities.IdRequest{Id: requestDataStruct.AppId}) | ||||||
| 		if errCode != errorcode.Success { | 		if errCode != errorcode.Success { | ||||||
| 			controllers.ErrWithCode(c, errCode) | 			controllers.ErrWithCode(c, errCode) | ||||||
| 			return | 			return | ||||||
|  | @ -162,12 +166,27 @@ func ValidatePayRequest() gin.HandlerFunc { | ||||||
| 		if cryptFunc == nil { | 		if cryptFunc == nil { | ||||||
| 			controllers.ErrWithCode(c, appCheck.GetCode()) | 			controllers.ErrWithCode(c, appCheck.GetCode()) | ||||||
| 		} | 		} | ||||||
| 		data, errCode := cryptFunc(app).Decrypt(commonDataStruct.Data) | 		dataByte, errCode := cryptFunc(app).Decrypt(requestDataStruct.Data) | ||||||
| 		if errCode != errorcode.Success { | 		if errCode != apicrypt.CryptNotError { | ||||||
| 			controllers.ErrWithCode(c, errCode) | 			controllers.ErrWithCode(c, errCode) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		c.Set("request", data) | 		//检查解密后的数据是否与请求一致
 | ||||||
|  | 		reCheck := appCheck.ReCheckAfterDecrypt(dataByte, requestDataStruct) | ||||||
|  | 		if !reCheck { | ||||||
|  | 			controllers.ErrWithCode(c, appCheck.GetCode()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		//表单验证
 | ||||||
|  | 		handler = requestmapping.FrontRequestMap[path] | ||||||
|  | 		v := handler() | ||||||
|  | 		msg, err := controllers.ValidApiData(dataByte, v) | ||||||
|  | 		if err != nil { | ||||||
|  | 			utils.Log(c, "参数错误", "path=", path, "err=", err.Error(), "msg=", msg) | ||||||
|  | 			controllers.Error(c, errorcode.ParamError, msg...) | ||||||
|  | 			c.Abort() | ||||||
|  | 		} | ||||||
|  | 		c.Set("request", dataByte) | ||||||
| 		c.Next() | 		c.Next() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,3 +8,9 @@ import ( | ||||||
| var FrontRequestMap = map[string]func() interface{}{ | var FrontRequestMap = map[string]func() interface{}{ | ||||||
| 	common.FRONT_V1 + "/pay/web": func() interface{} { return new(front.PayWeb) }, | 	common.FRONT_V1 + "/pay/web": func() interface{} { return new(front.PayWeb) }, | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func FrontRequest() func() interface{} { | ||||||
|  | 	return func() interface{} { | ||||||
|  | 		return new(front.RequestBody) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -4,8 +4,8 @@ package routes | ||||||
|  * 配置路由 |  * 配置路由 | ||||||
|  */ |  */ | ||||||
| import ( | import ( | ||||||
|  | 	"PaymentCenter/app/constants/common" | ||||||
| 	"PaymentCenter/app/http/controllers" | 	"PaymentCenter/app/http/controllers" | ||||||
| 	"PaymentCenter/app/http/controllers/backend" |  | ||||||
| 	"PaymentCenter/app/http/controllers/front" | 	"PaymentCenter/app/http/controllers/front" | ||||||
| 	"PaymentCenter/app/http/middlewares" | 	"PaymentCenter/app/http/middlewares" | ||||||
| 	"PaymentCenter/app/http/trace" | 	"PaymentCenter/app/http/trace" | ||||||
|  | @ -47,7 +47,7 @@ func RegisterRoute(router *gin.Engine) { | ||||||
| 
 | 
 | ||||||
| 	//router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
 | 	//router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
 | ||||||
| 
 | 
 | ||||||
| 	v1 := router.Group("/v1") | 	v1 := router.Group(common.FRONT_V1) | ||||||
| 	{ | 	{ | ||||||
| 		//回调处理
 | 		//回调处理
 | ||||||
| 		notify := v1.Group("/notify") | 		notify := v1.Group("/notify") | ||||||
|  | @ -55,9 +55,13 @@ func RegisterRoute(router *gin.Engine) { | ||||||
| 			notify.POST("/wx/:payChannelId", front.WxCallback) | 			notify.POST("/wx/:payChannelId", front.WxCallback) | ||||||
| 			notify.POST("/ali/:payChannelId", front.AliCallback) | 			notify.POST("/ali/:payChannelId", front.AliCallback) | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		pay := v1.Group("/pay", middlewares.ValidateRequest(), middlewares.ValidatePayRequest()) | ||||||
|  | 		{ | ||||||
|  | 			pay.POST("/web", front.WebPay) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) | 	router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) | ||||||
| 	pay := v1.Group("pay", middlewares.ValidatePayRequest()) | 
 | ||||||
| 	pay.POST("web", backend.MerchantList) // 商户列表
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,9 +3,11 @@ package services | ||||||
| import ( | import ( | ||||||
| 	"PaymentCenter/app/constants/errorcode" | 	"PaymentCenter/app/constants/errorcode" | ||||||
| 	"PaymentCenter/app/constants/pojo" | 	"PaymentCenter/app/constants/pojo" | ||||||
|  | 	"PaymentCenter/app/http/entities/front" | ||||||
| 	"PaymentCenter/app/models/appmodel" | 	"PaymentCenter/app/models/appmodel" | ||||||
| 	"PaymentCenter/app/services/apicrypt" | 	"PaymentCenter/app/services/apicrypt" | ||||||
| 	"PaymentCenter/app/utils" | 	"PaymentCenter/app/utils" | ||||||
|  | 	"github.com/bytedance/sonic" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -59,3 +61,16 @@ func (a *AppCheck) Crypt() (cryptFunc func(app *appmodel.App) apicrypt.ApiCrypt) | ||||||
| 	} | 	} | ||||||
| 	return cryptFunc | 	return cryptFunc | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (a *AppCheck) ReCheckAfterDecrypt(data []byte, requestData *front.RequestBody) bool { | ||||||
|  | 	var requestCommonData front.ApiCommonBody | ||||||
|  | 	if err := sonic.Unmarshal(data, &requestCommonData); err != nil { | ||||||
|  | 		a.Code = errorcode.ParamError | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	if requestCommonData.AppId != requestData.AppId || requestCommonData.Timestamp != requestData.Timestamp { | ||||||
|  | 		a.Code = errorcode.AppDecryptDataDiscrepancy | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -13,11 +13,13 @@ func NewRsa(app *appmodel.App) ApiCrypt { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *Rsa) Encrypt(decryptData string) (encryptData []byte, errCode int) { | func (r *Rsa) Encrypt(decryptData string) (encryptData []byte, errCode int) { | ||||||
| 	if r.App.MerchantPublicKey == "" { | 	if r.App.PublicKey == "" { | ||||||
| 		return nil, errorcode.AppRsaEncryptKeyNotFound | 		return nil, errorcode.AppRsaEncryptKeyNotFound | ||||||
| 	} | 	} | ||||||
| 	//
 | 	publicKeyPEM := `-----BEGIN PUBLIC KEY-----   | ||||||
| 	encryptData, err := rsa.Encrypt(r.App.MerchantPublicKey, []byte(decryptData)) | ` + r.App.PublicKey + `   | ||||||
|  | -----END PUBLIC KEY-----` | ||||||
|  | 	encryptData, err := rsa.Encrypt(publicKeyPEM, []byte(decryptData)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, errorcode.AppRsaEncryptFail | 		return nil, errorcode.AppRsaEncryptFail | ||||||
| 	} | 	} | ||||||
|  | @ -26,12 +28,15 @@ func (r *Rsa) Encrypt(decryptData string) (encryptData []byte, errCode int) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *Rsa) Decrypt(encryptData string) (decryptData []byte, errCode int) { | func (r *Rsa) Decrypt(encryptData string) (decryptData []byte, errCode int) { | ||||||
|  | 	 | ||||||
| 	if r.App.PrivateKey == "" { | 	if r.App.PrivateKey == "" { | ||||||
| 		return nil, errorcode.AppRsaDecryptKeyNotFound | 		return nil, errorcode.AppRsaDecryptKeyNotFound | ||||||
| 	} | 	} | ||||||
| 
 | 	privateKeyPEM := `-----BEGIN RSA PRIVATE KEY-----   | ||||||
| 	decryptData, err := rsa.Decrypt(r.App.PrivateKey, encryptData) | ` + r.App.PrivateKey + `   | ||||||
| 	if err != nil { | -----END RSA PRIVATE KEY-----` | ||||||
|  | 	decryptData, err := rsa.Decrypt(privateKeyPEM, encryptData) | ||||||
|  | 	if err != nil || decryptData == nil { | ||||||
| 		return nil, errorcode.AppRsaDecryptFail | 		return nil, errorcode.AppRsaDecryptFail | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
|  |  | ||||||
|  | @ -29,3 +29,5 @@ var ApiCryptMap = map[int32]func(app *appmodel.App) ApiCrypt{ | ||||||
| 	pojo.SM2: NewSm2, | 	pojo.SM2: NewSm2, | ||||||
| 	pojo.SM4: NewSm4, | 	pojo.SM4: NewSm4, | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | const CryptNotError = 0 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ import ( | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | @ -14,8 +13,8 @@ const ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestRsaEncrypt(t *testing.T) { | func TestRsaEncrypt(t *testing.T) { | ||||||
| 	fmt.Println(time.Now().UnixNano() / (1000000)) | 
 | ||||||
| 	//fmt.Printf("%s\n", encrypt())
 | 	fmt.Printf("%s\n", encrypt()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestRsaDecrypt(t *testing.T) { | func TestRsaDecrypt(t *testing.T) { | ||||||
|  | @ -28,7 +27,7 @@ func TestRsaDecrypt(t *testing.T) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func encrypt() string { | func encrypt() string { | ||||||
| 	data := "{\"name\":\"张三\",\"sex\":1,\"is_human\":true}" | 	data := "{\"app_id\":5476377146882523138,\"name\":\"张三\",\"sex\":1,\"is_human\":true,\"timestamp\":53612533412643}" | ||||||
| 	dataJson := []byte(data) | 	dataJson := []byte(data) | ||||||
| 	pub := `-----BEGIN PUBLIC KEY-----   | 	pub := `-----BEGIN PUBLIC KEY-----   | ||||||
| ` + PUB + `   | ` + PUB + `   | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue