<feat>api表单验证
This commit is contained in:
parent
8ead5e11e2
commit
b1a1a4cd50
|
@ -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解密失败",
|
||||||
|
|
|
@ -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