feat:完善项目

This commit is contained in:
henry
2021-09-28 11:47:19 +08:00
commit da7b3130fe
167 changed files with 456676 additions and 0 deletions

114
router/address.go Normal file
View File

@ -0,0 +1,114 @@
package router
import (
"SciencesServer/app/api"
"github.com/gin-gonic/gin"
)
func register(app *gin.Engine) {
apiPrefix := "/api"
g := app.Group(apiPrefix)
// 登录验证
g.Use(NeedLogin(AddSkipperURL([]string{
apiPrefix + "/captcha",
apiPrefix + "/account/login",
apiPrefix + "/account/logout",
}...)))
// 权限验证
//g.Use(NeedPermission(AddSkipperURL([]string{
// apiPrefix + "/captcha",
// apiPrefix + "/account/login",
// apiPrefix + "/account/logout",
//}...)))
// Captcha 验证码
g.GET("/captcha", new(api.Captcha).Captcha)
// Upload 上传管理
g.POST("/upload", new(api.Upload).Upload)
// Account 账户管理
account := g.Group("/account")
{
_api := new(api.Account)
account.POST("/login", _api.Login)
account.POST("/logout", _api.Logout)
}
// User 用户管理
user := g.Group("/user")
{
_api := new(api.User)
user.GET("/info", _api.Info)
user.GET("/menu", _api.Menu)
user.POST("/list", _api.List)
user.POST("/add", _api.Add)
user.POST("/edit", _api.Edit)
user.POST("/delete", _api.Delete)
user.POST("/edit/password", _api.EditPassword)
user.POST("/password/quick", _api.QuickPassword)
user.POST("/role", _api.Role)
user.POST("/role/bind", _api.RoleBind)
}
// Tenant 租户管理
tenant := g.Group("/tenant")
{
_api := new(api.Tenant)
tenant.POST("/list", _api.List)
tenant.POST("/add", _api.Add)
tenant.POST("/edit", _api.Edit)
tenant.POST("/edit/password", _api.EditPassword)
tenant.POST("/detail", _api.Detail)
tenant.POST("/renewal", _api.Renewal)
tenant.POST("/start_up", _api.StartUp)
tenant.POST("/disable", _api.Disable)
tenant.POST("/member/bind", _api.MemberBind)
tenant.POST("/menu", _api.Menu)
tenant.POST("/menu/bind", _api.MenuBind)
tenant.POST("/auth/bind", _api.AuthBind)
}
// Menu 菜单管理
menu := g.Group("/menu")
{
_api := new(api.Menu)
menu.GET("/list", _api.List)
menu.POST("/add", _api.Add)
menu.POST("/edit", _api.Edit)
menu.POST("/status", _api.Status)
menu.POST("/delete", _api.Delete)
}
// Auth 权限管理
auth := g.Group("/auth")
{
_api := new(api.Auth)
auth.POST("/list", _api.List)
}
// Department 部门管理
department := g.Group("/department")
{
_api := new(api.Department)
department.GET("/list", _api.List)
department.GET("/select", _api.Select)
department.POST("/add", _api.Add)
department.POST("/edit", _api.Edit)
department.POST("/delete", _api.Delete)
}
// Role 角色管理
role := g.Group("/role")
{
_api := new(api.Role)
role.POST("/list", _api.List)
role.POST("/select", _api.Select)
role.POST("/add", _api.Add)
role.POST("/edit", _api.Edit)
role.POST("/status", _api.Status)
role.POST("/delete", _api.Delete)
role.POST("/menu", _api.Menu)
role.POST("/menu/bind", _api.MenuBind)
role.POST("/auth", _api.Auth)
role.POST("/auth/bind", _api.AuthBind)
}
// Logs 日志管理
log := g.Group("/log")
{
_api := new(api.Log)
log.POST("/login", _api.Login)
}
}

111
router/auth.go Normal file
View File

@ -0,0 +1,111 @@
package router
import (
"SciencesServer/app/service"
"SciencesServer/config"
"SciencesServer/serve/cache"
cache2 "SciencesServer/serve/cache"
"SciencesServer/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()
}
}
// 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 !_session.IsAdmin {
if _session.TenantID > 0 {
if isExist, _ := cache.Cache.SIsMember(config.RedisKeyForTenant, _session.TenantKey); !isExist {
c.JSON(http.StatusForbidden, gin.H{"message": "租户/公司信息协议已到期或已被禁用,无权限访问!"})
c.Abort()
return
}
}
//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()
}
}
}

13
router/handle.go Normal file
View File

@ -0,0 +1,13 @@
package router
import (
"github.com/gin-gonic/gin"
)
func NeedSysLogInput(c *gin.Context) {
//session, _ := c.Get(config.TokenForSession)
//_session := session.(*service.Session)
//params, _ := c.Get("params")
//service.Publish(config.EventForSystemLogs, _session.Community, _session.UID, _session.Name, mode, event, content,
// params, c.ClientIP())
}

98
router/middleware.go Normal file
View File

@ -0,0 +1,98 @@
package router
import (
"SciencesServer/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("Recoverrequest【%s】 error【%v】\n", c.Request.URL, err)
c.JSON(http.StatusInternalServerError, gin.H{
"message": "Internal Server Error",
})
c.Abort()
return
}
}()
c.Next()
}
}

74
router/rate/ip.go Normal file
View 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()
}
}
}

67
router/router.go Normal file
View File

@ -0,0 +1,67 @@
package router
import (
"SciencesServer/config"
"SciencesServer/router/rate"
"io"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
)
type Router struct {
*Option
}
type (
Option struct {
Mode string
IsCors bool
*RateLimitConfig
}
// RouterLimitConfig 限流配置
RateLimitConfig struct {
IsRate bool `json:"is_rate"`
Limit int `json:"limit"`
Capacity int `json:"capacity"`
}
)
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"))
// 注册路由
register(app)
app.MaxMultipartMemory = 4 << 20
return app
}
func NewRouter(option *Option) *Router {
return &Router{option}
}