#12 resubmit middlerware

This commit is contained in:
新亮
2021-03-06 13:28:38 +08:00
parent 499e171d63
commit 5d7975056c
21 changed files with 318 additions and 143 deletions
+4
View File
@@ -13,6 +13,8 @@ const (
ParamBindError = 10103
AuthorizationError = 10104
CallHTTPError = 10105
ResubmitError = 10106
ResubmitMsg = 10107
// 模块级错误码 - 用户模块
IllegalUserName = 20101
@@ -29,6 +31,8 @@ var codeText = map[int]string{
ParamBindError: "参数信息有误",
AuthorizationError: "签名信息有误",
CallHTTPError: "调用第三方 HTTP 接口失败",
ResubmitError: "Resubmit Error",
ResubmitMsg: "请勿重复提交",
IllegalUserName: "非法用户名",
UserCreateError: "创建用户失败",
@@ -28,7 +28,7 @@ type authResponse struct {
func (h *handler) Auth() core.HandlerFunc {
return func(c core.Context) {
cfg := configs.Get().JWT
tokenString, err := token.New(cfg.Secret).Sign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
tokenString, err := token.New(cfg.Secret).JwtSign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
@@ -1,4 +1,4 @@
package auth
package middleware
import (
"net/http"
@@ -12,7 +12,7 @@ import (
"github.com/pkg/errors"
)
func AuthHandler(ctx core.Context) (userId int64, userName string, err errno.Error) {
func (m *middleware) Jwt(ctx core.Context) (userId int64, userName string, err errno.Error) {
auth := ctx.GetHeader("Authorization")
if auth == "" {
err = errno.NewError(
@@ -24,7 +24,7 @@ func AuthHandler(ctx core.Context) (userId int64, userName string, err errno.Err
}
cfg := configs.Get().JWT
claims, errParse := token.New(cfg.Secret).Parse(auth)
claims, errParse := token.New(cfg.Secret).JwtParse(auth)
if errParse != nil {
err = errno.NewError(
http.StatusUnauthorized,
@@ -0,0 +1,70 @@
package middleware
import (
"net/http"
"time"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/token"
"github.com/pkg/errors"
)
func (m *middleware) Resubmit() core.HandlerFunc {
redisKeyPrefix := configs.ProjectName() + ":request-id:"
return func(c core.Context) {
cfg := configs.Get().URLToken
tokenString, err := token.New(cfg.Secret).UrlSign(c.Path(), c.Method(), c.RequestInputParams())
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ResubmitError,
code.Text(code.ResubmitError)).WithErr(err),
)
return
}
redisKey := redisKeyPrefix + tokenString
if !m.cache.Exists(redisKey) {
err = m.cache.Set(redisKey, "1", time.Minute*cfg.ExpireDuration)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ResubmitError,
code.Text(code.ResubmitError)).WithErr(err),
)
return
}
return
}
redisValue, err := m.cache.Get(redisKey, cache.WithTrace(c.Trace()))
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ResubmitError,
code.Text(code.ResubmitError)).WithErr(err),
)
return
}
if redisValue == "1" {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ResubmitMsg,
code.Text(code.ResubmitMsg)).WithErr(errors.New("resubmit")),
)
return
}
return
}
}
@@ -0,0 +1,40 @@
package middleware
import (
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/grpc"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"go.uber.org/zap"
)
var _ Middleware = (*middleware)(nil)
type Middleware interface {
// i 为了避免被其他包实现
i()
// JWT 中间件
Jwt(ctx core.Context) (userId int64, userName string, err errno.Error)
// Resubmit 中间件
Resubmit() core.HandlerFunc
}
type middleware struct {
logger *zap.Logger
cache cache.Repo
grpConn grpc.ClientConn
userService user_service.UserService
}
func New(logger *zap.Logger, cache cache.Repo) Middleware {
return &middleware{
logger: logger,
cache: cache,
}
}
func (m *middleware) i() {}
+23 -21
View File
@@ -3,7 +3,7 @@ package router
import (
"github.com/xinliangnote/go-gin-api/internal/api/controller/demo_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/user_handler"
"github.com/xinliangnote/go-gin-api/internal/api/router/middleware/auth"
"github.com/xinliangnote/go-gin-api/internal/api/router/middleware"
"github.com/xinliangnote/go-gin-api/internal/graph/handler"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
@@ -33,6 +33,10 @@ func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.C
panic(err)
}
// 中间件
middles := middleware.New(logger, cache)
// graphQL 控制器
gqlHandler := handler.New(logger, db, cache)
gql := mux.Group("/graphql")
@@ -41,11 +45,26 @@ func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.C
gql.POST("/query", gqlHandler.Query())
}
// demo 控制器
demoHandler := demo_handler.New(logger, db, cache, grpConn)
userHandler := user_handler.New(logger, db, cache)
demo := mux.Group("/demo", core.WrapAuthHandler(middles.Jwt)) // 使用 jwt 验证
{
// 为了演示 Trace ,增加了一些看起来无意义的调试信息和 SQL 信息。
demo.GET("/trace", demoHandler.Trace())
// user_demo CURD
user := mux.Group("/user", core.WrapAuthHandler(auth.AuthHandler))
// 模拟数据
demo.GET("get/:name", core.AliasForRecordMetrics("/demo/get"), demoHandler.Get())
demo.POST("post", demoHandler.Post())
}
demoNoAuth := mux.Group("/auth") // 不使用 jwt 验证
{
demoNoAuth.POST("/get", demoHandler.Auth())
}
// user 控制器
userHandler := user_handler.New(logger, db, cache)
user := mux.Group("/user", core.WrapAuthHandler(middles.Jwt))
{
user.POST("/create", userHandler.Create())
user.PUT("/update", userHandler.UpdateNickNameByID())
@@ -53,22 +72,5 @@ func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.C
user.GET("/info/:username", core.AliasForRecordMetrics("/user/info"), userHandler.Detail())
}
// auth
a := mux.Group("/auth")
{
a.POST("/get", demoHandler.Auth())
}
// demo
d := mux.Group("/demo", core.WrapAuthHandler(auth.AuthHandler)) //使用 auth 验证
{
// 为了演示 Trace ,增加了一些看起来无意义的调试信息和 SQL 信息。
d.GET("/trace", demoHandler.Trace())
// 模拟数据
d.GET("get/:name", core.AliasForRecordMetrics("/demo/get"), demoHandler.Get())
d.POST("post", demoHandler.Post())
}
return mux, nil
}
+10
View File
@@ -34,6 +34,7 @@ type Repo interface {
Expire(key string, ttl time.Duration) bool
ExpireAt(key string, ttl time.Time) bool
Del(keys ...string) bool
Exists(keys ...string) bool
Incr(key string, options ...Option) int64
Close() error
}
@@ -148,6 +149,15 @@ func (c *cacheRepo) ExpireAt(key string, ttl time.Time) bool {
return ok
}
//
func (c *cacheRepo) Exists(keys ...string) bool {
if len(keys) == 0 {
return true
}
value, _ := c.client.Exists(keys...).Result()
return value > 0
}
// Del del some key from redis
func (c *cacheRepo) Del(keys ...string) bool {
if len(keys) == 0 {
+16
View File
@@ -120,6 +120,10 @@ type Context interface {
Alias() string
setAlias(path string)
// RequestInputParams 获取所有参数
RequestInputParams() url.Values
// RequestPostFormParams 获取 PostForm 参数
RequestPostFormParams() url.Values
// Request 获取 Request 对象
Request() *http.Request
// RawData 获取 Request.Body
@@ -326,6 +330,18 @@ func (c *context) setAlias(path string) {
}
}
// RequestInputParams 获取所有参数
func (c *context) RequestInputParams() url.Values {
_ = c.ctx.Request.ParseForm()
return c.ctx.Request.Form
}
// RequestPostFormParams 获取 PostForm 参数
func (c *context) RequestPostFormParams() url.Values {
_ = c.ctx.Request.ParseForm()
return c.ctx.Request.PostForm
}
// Request 获取 Request
func (c *context) Request() *http.Request {
return c.ctx.Request