init
This commit is contained in:
127
router/auth.go
Normal file
127
router/auth.go
Normal file
@ -0,0 +1,127 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"Edu/app/service"
|
||||
"Edu/config"
|
||||
cache2 "Edu/serve/cache"
|
||||
"Edu/utils"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SkipperURL 跳过验证
|
||||
type SkipperURL func(*gin.Context) bool
|
||||
|
||||
// PermissionHandle 权限验证
|
||||
type PermissionHandle func(key string) gin.HandlerFunc
|
||||
|
||||
// AddSkipperURL 添加路由
|
||||
func AddSkipperURL(url ...string) SkipperURL {
|
||||
return func(c *gin.Context) bool {
|
||||
path := c.Request.URL.Path
|
||||
return utils.InArray(path, url)
|
||||
}
|
||||
}
|
||||
|
||||
// NeedLogin 需要登录
|
||||
func NeedLogin(skipperURL ...SkipperURL) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if len(skipperURL) > 0 && skipperURL[0](c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
token := c.GetHeader(config.APIRequestToken)
|
||||
|
||||
if token == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"message": "Token异常"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
tokenInfo := utils.JWTDecrypt(token)
|
||||
|
||||
if tokenInfo == nil || len(tokenInfo) <= 0 {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"message": "Token无效"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
expTimestamp := utils.StringToInt64(fmt.Sprintf("%v", tokenInfo["exp"]))
|
||||
expTime := time.Unix(expTimestamp, 0)
|
||||
ok := expTime.After(time.Now())
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"message": "Token过期"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
cache, _ := cache2.Cache.HGet(config.RedisKeyForAccount, fmt.Sprintf("%v", tokenInfo[config.TokenForUID]))
|
||||
|
||||
if cache == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"message": "用户未登录或已退出"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
session := new(service.Session)
|
||||
_ = session.UnmarshalBinary([]byte(cache))
|
||||
|
||||
if !config.SettingInfo.MultipleLogin && session.Token != token {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"message": "登录失效,已在其他地方登录!"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set(config.TokenForSession, session)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// NeedGradeParam 需要年级参数
|
||||
func NeedGradeParam(skipperURL ...SkipperURL) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if len(skipperURL) > 0 && skipperURL[0](c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
param := c.GetHeader(config.APIRequestGrade)
|
||||
|
||||
if param == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数异常"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
grade := utils.StringToInt(param)
|
||||
|
||||
if grade <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "Grade异常"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set(config.TokenForGrade, grade)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// NeedPermission 需要权限验证
|
||||
func NeedPermission(skipperURL ...SkipperURL) PermissionHandle {
|
||||
return func(key string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if len(skipperURL) > 0 && skipperURL[0](c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
//session, _ := c.Get(config.TokenForSession)
|
||||
//_session := session.(*service.Session)
|
||||
|
||||
//if pass, _ := service.NewPermission(nil, &service.AuthRequest{
|
||||
// Url: key,
|
||||
// Method: c.Request.Method,
|
||||
//})(_session.TenantKey, fmt.Sprintf("%d", _session.UID)).Enforce(); !pass {
|
||||
// c.JSON(http.StatusOK, gin.H{"code": http.StatusForbidden, "msg": "无权限访问!"})
|
||||
// c.Abort()
|
||||
// return
|
||||
//}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
}
|
115
router/middleware.go
Normal file
115
router/middleware.go
Normal file
@ -0,0 +1,115 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"Edu/config"
|
||||
"Edu/serve/logger"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Headers", "access-control-allow-origin, access-control-allow-headers, application/octet-stream")
|
||||
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// NoMethodHandler 未找到请求方法的处理函数
|
||||
func NoMethodHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.JSON(http.StatusMethodNotAllowed, gin.H{
|
||||
"message": "未找到请求路由的处理函数",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// NoRouteHandler 未找到请求路由的处理函数
|
||||
func NoRouteHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"message": "未找到请求路由的处理函数",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func LoggerHandle(log string, leastDay uint) gin.HandlerFunc {
|
||||
_logger := logger.NewLogger().Init(&logger.Option{File: log, LeastDay: leastDay, Level: "debug", IsStdout: false}).Logger
|
||||
|
||||
return func(c *gin.Context) {
|
||||
_logger.Log(logrus.InfoLevel, map[string]interface{}{
|
||||
"Status": c.Writer.Status(),
|
||||
"IP": c.ClientIP(),
|
||||
"Method": c.Request.Method,
|
||||
"Url": c.Request.RequestURI,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TimeoutHandle 超时处理
|
||||
func TimeoutHandle(timeout time.Duration) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
|
||||
|
||||
defer func() {
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
c.Writer.WriteHeader(http.StatusGatewayTimeout)
|
||||
c.Abort()
|
||||
}
|
||||
cancel()
|
||||
}()
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// RecoveryHandler 崩溃恢复中间件
|
||||
func RecoveryHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Printf("Recover:request【%s】 error:【%v】\n", c.Request.URL, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": "Internal Server Error!",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// AnalysisParamsHandler 解析参数
|
||||
func AnalysisParamsHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
url := c.Request.URL
|
||||
|
||||
var data string
|
||||
|
||||
value, has := config.SystemConfig[url.String()]
|
||||
|
||||
if has {
|
||||
data = value.(string)
|
||||
}
|
||||
c.Set(config.ContentForLocal, data)
|
||||
}
|
||||
}
|
74
router/rate/ip.go
Normal file
74
router/rate/ip.go
Normal file
@ -0,0 +1,74 @@
|
||||
package rate
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// IPRateLimiter IP限流器
|
||||
type IPRateLimiter struct {
|
||||
ips map[string]*rate.Limiter // IP地址库
|
||||
limit int
|
||||
capacity int
|
||||
mutex *sync.RWMutex // 锁
|
||||
}
|
||||
|
||||
type AllowHandle func(ctx *gin.Context) bool
|
||||
|
||||
type IPRateLimiterConfig func(limit, capacity int) gin.HandlerFunc
|
||||
|
||||
var IPRateLimiterHandle *IPRateLimiter
|
||||
|
||||
// Handle
|
||||
func (this *IPRateLimiter) Handle() AllowHandle {
|
||||
return func(ctx *gin.Context) bool {
|
||||
ip := ctx.ClientIP()
|
||||
|
||||
this.mutex.RLock()
|
||||
defer this.mutex.RUnlock()
|
||||
|
||||
limiter, exists := this.ips[ip]
|
||||
|
||||
if !exists {
|
||||
limiter = rate.NewLimiter(rate.Limit(this.limit), this.capacity)
|
||||
this.ips[ip] = limiter
|
||||
}
|
||||
return limiter.Allow()
|
||||
}
|
||||
}
|
||||
|
||||
// NewIPRateLimiter 初始化限流器
|
||||
func NewIPRateLimiter() IPRateLimiterConfig {
|
||||
return func(limit, capacity int) gin.HandlerFunc {
|
||||
IPRateLimiterHandle = &IPRateLimiter{
|
||||
ips: make(map[string]*rate.Limiter, 0),
|
||||
limit: limit,
|
||||
capacity: capacity,
|
||||
mutex: new(sync.RWMutex),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RequestIPRateLimiter 请求限流
|
||||
func RequestIPRateLimiter() IPRateLimiterConfig {
|
||||
return func(limit, capacity int) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if IPRateLimiterHandle == nil {
|
||||
NewIPRateLimiter()(limit, capacity)
|
||||
}
|
||||
if !IPRateLimiterHandle.Handle()(c) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": http.StatusTooManyRequests,
|
||||
"msg": "访问频率过快,请稍后访问!",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
}
|
126
router/router.go
Normal file
126
router/router.go
Normal file
@ -0,0 +1,126 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"Edu/app/api"
|
||||
"Edu/config"
|
||||
"Edu/router/rate"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Router struct {
|
||||
*Option
|
||||
handler *gin.Engine
|
||||
}
|
||||
|
||||
type (
|
||||
Option struct {
|
||||
Mode string
|
||||
IsCors bool
|
||||
*RateLimitConfig
|
||||
}
|
||||
// RateLimitConfig 限流配置
|
||||
RateLimitConfig struct {
|
||||
IsRate bool `json:"is_rate"`
|
||||
Limit int `json:"limit"`
|
||||
Capacity int `json:"capacity"`
|
||||
}
|
||||
)
|
||||
|
||||
func (this *Router) registerAPI() {
|
||||
apiPrefix := "/api"
|
||||
g := this.handler.Group(apiPrefix)
|
||||
g.Use()
|
||||
// 登录验证
|
||||
g.Use(NeedLogin(AddSkipperURL([]string{
|
||||
apiPrefix + "/v1/account/authorize",
|
||||
apiPrefix + "/v1/account/tourist",
|
||||
apiPrefix + "/v1/index",
|
||||
apiPrefix + "/v1/grade",
|
||||
apiPrefix + "/v1/book/list",
|
||||
}...)))
|
||||
g.Use(NeedGradeParam(AddSkipperURL([]string{
|
||||
apiPrefix + "/v1/account/authorize",
|
||||
apiPrefix + "/v1/account/tourist",
|
||||
apiPrefix + "/v1/grade",
|
||||
}...)))
|
||||
v1 := g.Group("/v1")
|
||||
// Account 接口管理
|
||||
account := v1.Group("/account")
|
||||
{
|
||||
_api := new(api.Account)
|
||||
account.POST("/authorize", _api.Authorize)
|
||||
|
||||
if config.IsDebug() {
|
||||
account.GET("/tourist", _api.Tourist)
|
||||
}
|
||||
}
|
||||
// Banner 接口管理
|
||||
v1.GET("/index", new(api.Index).Index)
|
||||
v1.GET("/grade", new(api.Index).Grade)
|
||||
v1.GET("/banner", new(api.Banner).List)
|
||||
v1.GET("/book/list", new(api.Book).List)
|
||||
// User 接口管理
|
||||
user := v1.Group("/user")
|
||||
{
|
||||
_api := new(api.User)
|
||||
user.GET("/info", _api.Info)
|
||||
}
|
||||
// Book 接口管理
|
||||
book := v1.Group("/book")
|
||||
{
|
||||
_api := new(api.Book)
|
||||
book.GET("/home", _api.Home)
|
||||
//book.GET("/list", _api.List)
|
||||
book.GET("/detail", _api.Detail)
|
||||
book.POST("/play", _api.Play)
|
||||
}
|
||||
// Pay 支付管理
|
||||
pay := v1.Group("/pay")
|
||||
{
|
||||
_api := new(api.Pay)
|
||||
pay.POST("/launch", _api.Launch)
|
||||
pay.POST("/notice", _api.Notify)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Router) Init() *gin.Engine {
|
||||
gin.SetMode(this.Mode)
|
||||
|
||||
app := gin.New()
|
||||
|
||||
if this.IsCors {
|
||||
app.Use(Cors())
|
||||
}
|
||||
app.NoRoute(NoRouteHandler())
|
||||
app.NoMethod(NoMethodHandler())
|
||||
app.Use(LoggerHandle("log/gin.log", 3))
|
||||
app.Use(TimeoutHandle(time.Second * 30))
|
||||
app.Use(RecoveryHandler())
|
||||
|
||||
if this.RateLimitConfig != nil {
|
||||
if this.RateLimitConfig.IsRate {
|
||||
rate.NewIPRateLimiter()(this.RateLimitConfig.Limit, this.RateLimitConfig.Capacity)
|
||||
app.Use(rate.RequestIPRateLimiter()(this.RateLimitConfig.Limit, this.RateLimitConfig.Capacity))
|
||||
}
|
||||
}
|
||||
if config.IsDebug() {
|
||||
gin.DefaultWriter = io.MultiWriter(os.Stdout)
|
||||
app.StaticFS("/api-docs", http.Dir("./doc"))
|
||||
}
|
||||
app.StaticFS("/upload", http.Dir("./upload"))
|
||||
app.MaxMultipartMemory = 4 << 20
|
||||
this.handler = app
|
||||
// 注册路由
|
||||
this.registerAPI()
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func NewRouter(option *Option) *Router {
|
||||
return &Router{Option: option}
|
||||
}
|
Reference in New Issue
Block a user