<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

@ -30,21 +30,22 @@ const (
// app
AppNotFound = 1200
AppDisabled = 1201
AppIpNotAllow = 1202
AppRsaDecryptKeyNotFound = 1203
AppRsaDecryptFail = 1210
AppRsaEncryptKeyNotFound = 1211
AppRsaEncryptFail = 1212
AppSM2DecryptKeyNotFound = 1220
AppSM2DecryptFail = 1221
AppSM2EncryptKeyNotFound = 1222
AppSM2EncryptFail = 1223
AppSM4DecryptKeyNotFound = 1230
AppSM4DecryptFail = 1231
AppSM4EncryptKeyNotFound = 1232
AppSM4EncryptFail = 1233
AppNotFound = 1200
AppDisabled = 1201
AppIpNotAllow = 1202
AppRsaDecryptKeyNotFound = 1203
AppDecryptDataDiscrepancy = 1204
AppRsaDecryptFail = 1210
AppRsaEncryptKeyNotFound = 1211
AppRsaEncryptFail = 1212
AppSM2DecryptKeyNotFound = 1220
AppSM2DecryptFail = 1221
AppSM2EncryptKeyNotFound = 1222
AppSM2EncryptFail = 1223
AppSM4DecryptKeyNotFound = 1230
AppSM4DecryptFail = 1231
AppSM4EncryptKeyNotFound = 1232
AppSM4EncryptFail = 1233
//渠道
PayChannelNotFound = 1300
@ -60,16 +61,17 @@ var MsgEN = map[int]string{
}
var MsgZH = map[int]string{
Success: "请求成功",
ParamError: "参数错误",
NotFound: "数据不存在",
NotAuth: "未经授权",
NotLogin: "未登录",
RequestTimeOut: "请求超时",
MerchantNotFound: "商户不存在",
AppNotFound: "app_id未找到",
AppDisabled: "app通道关闭",
AppIpNotAllow: "ip不在白名单内",
Success: "请求成功",
ParamError: "参数错误",
NotFound: "数据不存在",
NotAuth: "未经授权",
NotLogin: "未登录",
RequestTimeOut: "请求超时",
MerchantNotFound: "商户不存在",
AppNotFound: "app_id未找到",
AppDisabled: "app通道关闭",
AppIpNotAllow: "ip不在白名单内",
AppDecryptDataDiscrepancy: "解密数据不一致",
AppRsaDecryptKeyNotFound: "密匙缺失无法进行Rsa解密",
AppRsaDecryptFail: "Rsa解密失败",

View File

@ -149,6 +149,34 @@ func GenRequest(c *gin.Context, request interface{}) (msgs []string, err error)
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
func ReadBody(c *gin.Context) (body []byte, err error) {
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
type PayCommonBody struct {
type ApiCommonBody struct {
AppId int64 `json:"app_id" validate:"required"`
Timestamp int64 `json:"timestamp" validate:"required"`
}
@ -12,7 +12,7 @@ type RequestBody struct {
}
type PayWeb struct {
PayCommonBody
PayChannel int64 `json:"private_key_path"`
ApiCommonBody
PayChannel int64 `json:"private_key_path" validate:"required" label:"支付渠道"`
Delay int64 `json:"delay"` //延时时间
}

View File

@ -8,6 +8,7 @@ import (
"PaymentCenter/app/http/entities/front"
"PaymentCenter/app/http/requestmapping"
"PaymentCenter/app/services"
"PaymentCenter/app/services/apicrypt"
"PaymentCenter/app/utils"
"PaymentCenter/config"
"github.com/gin-gonic/gin"
@ -107,7 +108,7 @@ func ValidateRequest() gin.HandlerFunc {
if strings.Index(path, "admin") >= 0 {
handler = requestmapping.BackendRequestMap[path]
} else {
handler = requestmapping.FrontRequestMap[path]
handler = requestmapping.FrontRequest()
}
if handler == nil {
utils.Log(c, "path", path)
@ -129,11 +130,14 @@ func ValidateRequest() gin.HandlerFunc {
func ValidatePayRequest() gin.HandlerFunc {
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 {
controllers.ErrWithCode(c, errorcode.ParamError)
return
}
commonDataStruct := commonData.(*front.RequestBody)
requestDataStruct := requestData.(*front.RequestBody)
//判断时间
//now := time.Now().UnixNano() / 1000000
//if comStruct.Timestamp > now || (config.GetConf().TimeOut != 0 && (now-comStruct.Timestamp) > config.GetConf().TimeOut) {
@ -141,7 +145,7 @@ func ValidatePayRequest() gin.HandlerFunc {
// return
//}
//获取app信息
app, errCode := services.AppFindOne(entities.IdRequest{Id: commonDataStruct.AppId})
app, errCode := services.AppFindOne(entities.IdRequest{Id: requestDataStruct.AppId})
if errCode != errorcode.Success {
controllers.ErrWithCode(c, errCode)
return
@ -162,12 +166,27 @@ func ValidatePayRequest() gin.HandlerFunc {
if cryptFunc == nil {
controllers.ErrWithCode(c, appCheck.GetCode())
}
data, errCode := cryptFunc(app).Decrypt(commonDataStruct.Data)
if errCode != errorcode.Success {
dataByte, errCode := cryptFunc(app).Decrypt(requestDataStruct.Data)
if errCode != apicrypt.CryptNotError {
controllers.ErrWithCode(c, errCode)
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()
}
}

View File

@ -8,3 +8,9 @@ import (
var FrontRequestMap = map[string]func() interface{}{
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 (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/http/controllers"
"PaymentCenter/app/http/controllers/backend"
"PaymentCenter/app/http/controllers/front"
"PaymentCenter/app/http/middlewares"
"PaymentCenter/app/http/trace"
@ -47,7 +47,7 @@ func RegisterRoute(router *gin.Engine) {
//router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
v1 := router.Group("/v1")
v1 := router.Group(common.FRONT_V1)
{
//回调处理
notify := v1.Group("/notify")
@ -55,9 +55,13 @@ func RegisterRoute(router *gin.Engine) {
notify.POST("/wx/:payChannelId", front.WxCallback)
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))
pay := v1.Group("pay", middlewares.ValidatePayRequest())
pay.POST("web", backend.MerchantList) // 商户列表
}

View File

@ -3,9 +3,11 @@ package services
import (
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/constants/pojo"
"PaymentCenter/app/http/entities/front"
"PaymentCenter/app/models/appmodel"
"PaymentCenter/app/services/apicrypt"
"PaymentCenter/app/utils"
"github.com/bytedance/sonic"
"strings"
)
@ -59,3 +61,16 @@ func (a *AppCheck) Crypt() (cryptFunc func(app *appmodel.App) apicrypt.ApiCrypt)
}
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) {
if r.App.MerchantPublicKey == "" {
if r.App.PublicKey == "" {
return nil, errorcode.AppRsaEncryptKeyNotFound
}
//
encryptData, err := rsa.Encrypt(r.App.MerchantPublicKey, []byte(decryptData))
publicKeyPEM := `-----BEGIN PUBLIC KEY-----
` + r.App.PublicKey + `
-----END PUBLIC KEY-----`
encryptData, err := rsa.Encrypt(publicKeyPEM, []byte(decryptData))
if err != nil {
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) {
if r.App.PrivateKey == "" {
return nil, errorcode.AppRsaDecryptKeyNotFound
}
decryptData, err := rsa.Decrypt(r.App.PrivateKey, encryptData)
if err != nil {
privateKeyPEM := `-----BEGIN RSA PRIVATE KEY-----
` + r.App.PrivateKey + `
-----END RSA PRIVATE KEY-----`
decryptData, err := rsa.Decrypt(privateKeyPEM, encryptData)
if err != nil || decryptData == nil {
return nil, errorcode.AppRsaDecryptFail
}
return

View File

@ -29,3 +29,5 @@ var ApiCryptMap = map[int32]func(app *appmodel.App) ApiCrypt{
pojo.SM2: NewSm2,
pojo.SM4: NewSm4,
}
const CryptNotError = 0

View File

@ -4,7 +4,6 @@ import (
"encoding/base64"
"fmt"
"testing"
"time"
)
const (
@ -14,8 +13,8 @@ const (
)
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) {
@ -28,7 +27,7 @@ func TestRsaDecrypt(t *testing.T) {
}
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)
pub := `-----BEGIN PUBLIC KEY-----
` + PUB + `