forked from lei.chen/myxce
143 lines
3.5 KiB
Go
143 lines
3.5 KiB
Go
package middleware
|
|
|
|
import (
|
|
"apiserver/common"
|
|
"apiserver/controllers"
|
|
"apiserver/models"
|
|
"apiserver/utils"
|
|
logger "apiserver/utils"
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt/v4"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func ValidateHandler() gin.HandlerFunc {
|
|
logger.Debug("Enter ValidateHandler() function")
|
|
return func(ctx *gin.Context) {
|
|
if isExcludedPath(ctx.Request.URL.Path) {
|
|
ctx.Next()
|
|
return
|
|
}
|
|
|
|
validateHeader()(ctx)
|
|
if ctx.IsAborted() {
|
|
return
|
|
}
|
|
flag := !strings.Contains("multipart/form-data", ctx.Request.Header.Get("Content-Type"))
|
|
if ctx.Request.Method == http.MethodPost && flag == false {
|
|
isValidBody, err := validateRequestBody(ctx)
|
|
if err != nil || !isValidBody {
|
|
controllers.ReturnError(ctx, common.INVALID_PARAMS_CODE,
|
|
common.ERROR_READ_REQUEST_BODY_MSG+": "+err.Error(), nil)
|
|
return
|
|
}
|
|
}
|
|
if ctx.IsAborted() {
|
|
return
|
|
}
|
|
ctx.Next()
|
|
}
|
|
}
|
|
|
|
func isExcludedPath(path string) bool {
|
|
|
|
allowed := false
|
|
// define some excluded paths
|
|
excludedPaths := []string{
|
|
"/ping",
|
|
"/xce/v1/auth/logon",
|
|
"/swagger/*any",
|
|
"/xce/v1/auth/logout",
|
|
}
|
|
for _, excludedPath := range excludedPaths {
|
|
if strings.HasPrefix(path, excludedPath[:len(excludedPath)-1]) { // 去掉最后的 *
|
|
allowed = true
|
|
break
|
|
}
|
|
}
|
|
return allowed
|
|
}
|
|
|
|
// Token validation middleware
|
|
func validateHeader() gin.HandlerFunc {
|
|
logger.Debug("Enter validateHeader() function")
|
|
return func(ctx *gin.Context) {
|
|
//continue to validate token
|
|
token := ctx.GetHeader("Authorization")
|
|
if token == "" {
|
|
logger.Error("Token is empty")
|
|
controllers.ReturnError(ctx, common.ERROR_AUTH_CHECK_TOKEN_CODE,
|
|
common.ERROR_AUTH_CHECK_TOKEN_EMPTY_MSG, nil)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
|
|
// Validate token (this is a placeholder for your actual token validation logic)
|
|
isValid, err := isValidToken(strings.TrimPrefix(token, "Bearer "), ctx.ClientIP())
|
|
if err != nil {
|
|
logger.Error("Validate token error", zap.Error(err))
|
|
controllers.ReturnError(ctx, common.ERROR_AUTH_CHECK_TOKEN_CODE,
|
|
common.ERROR_AUTH_MSG+": "+err.Error(), nil)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
if !isValid {
|
|
logger.Error("Token is invalid")
|
|
controllers.ReturnError(ctx, common.ERROR_AUTH_CHECK_TOKEN_CODE,
|
|
common.ERROR_AUTH_MSG, nil)
|
|
ctx.Abort()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func isValidToken(tokenString string, IP string) (bool, error) {
|
|
token, err := jwt.ParseWithClaims(tokenString, &models.JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
return []byte(utils.AuthenticationYamlConfig.Token.SecretKey), nil
|
|
})
|
|
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if claims, ok := token.Claims.(*models.JWTClaims); ok && token.Valid {
|
|
if claims.IP == IP {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func validateRequestBody(ctx *gin.Context) (bool, error) {
|
|
logger.Debug("Enter validateRequestBody function")
|
|
|
|
// read request body and resolve EOF error
|
|
body, err := ctx.GetRawData()
|
|
if err != nil {
|
|
logger.Error("Failed to read request body", zap.Error(err))
|
|
ctx.Abort()
|
|
return false, err
|
|
}
|
|
if len(body) == 0 {
|
|
logger.Error("Request body is empty")
|
|
ctx.Abort()
|
|
return false, errors.New(common.ERROR_REQUEST_BODY_EMPTY_MSG)
|
|
}
|
|
|
|
// reset request body
|
|
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
|
// check request body is valid
|
|
if !json.Valid(body) {
|
|
logger.Error("Request body is not valid", zap.Error(err))
|
|
ctx.Abort()
|
|
return false, errors.New(common.ERROR_REQUEST_BODY_NOT_JSON_MSG)
|
|
}
|
|
return true, nil
|
|
}
|