<feat>api表单验证
This commit is contained in:
parent
8ead5e11e2
commit
b1a1a4cd50
|
@ -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解密失败",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
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"` //延时时间
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) // 商户列表
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -29,3 +29,5 @@ var ApiCryptMap = map[int32]func(app *appmodel.App) ApiCrypt{
|
|||
pojo.SM2: NewSm2,
|
||||
pojo.SM4: NewSm4,
|
||||
}
|
||||
|
||||
const CryptNotError = 0
|
||||
|
|
|
@ -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 + `
|
||||
|
|
Loading…
Reference in New Issue