<feat>api表单验证

This commit is contained in:
Rzy 2024-08-05 14:05:46 +08:00
parent 8ead5e11e2
commit b1a1a4cd50
11 changed files with 139 additions and 49 deletions

View File

@ -34,6 +34,7 @@ const (
AppDisabled = 1201 AppDisabled = 1201
AppIpNotAllow = 1202 AppIpNotAllow = 1202
AppRsaDecryptKeyNotFound = 1203 AppRsaDecryptKeyNotFound = 1203
AppDecryptDataDiscrepancy = 1204
AppRsaDecryptFail = 1210 AppRsaDecryptFail = 1210
AppRsaEncryptKeyNotFound = 1211 AppRsaEncryptKeyNotFound = 1211
AppRsaEncryptFail = 1212 AppRsaEncryptFail = 1212
@ -70,6 +71,7 @@ var MsgZH = map[int]string{
AppNotFound: "app_id未找到", AppNotFound: "app_id未找到",
AppDisabled: "app通道关闭", AppDisabled: "app通道关闭",
AppIpNotAllow: "ip不在白名单内", AppIpNotAllow: "ip不在白名单内",
AppDecryptDataDiscrepancy: "解密数据不一致",
AppRsaDecryptKeyNotFound: "密匙缺失无法进行Rsa解密", AppRsaDecryptKeyNotFound: "密匙缺失无法进行Rsa解密",
AppRsaDecryptFail: "Rsa解密失败", AppRsaDecryptFail: "Rsa解密失败",

View File

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

View File

@ -0,0 +1,10 @@
package front
import (
"fmt"
"github.com/gin-gonic/gin"
)
func WebPay(c *gin.Context) {
fmt.Println("312312312321")
}

View File

@ -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"` //延时时间
} }

View File

@ -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()
} }
} }

View File

@ -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)
}
}

View File

@ -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) // 商户列表
} }

View File

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

View File

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

View File

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

View File

@ -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 + `