forked from all_user/stat_center
Initial commit
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/utils"
|
||||
"stat/middleware/define"
|
||||
"stat/models/adminMod"
|
||||
"stat/service/agentSer"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// gin 验证中间件
|
||||
|
||||
var (
|
||||
// 白名单,不需要验证token的api
|
||||
whiteList = []string{
|
||||
"/swagger/*",
|
||||
"/swagger",
|
||||
"/favicon.ico",
|
||||
"/api/stat/admin/login",
|
||||
"/api/stat/admin/verify",
|
||||
"/api/stat/agent/imgVerify",
|
||||
"/api/stat/agent/login",
|
||||
"/api/stat/agent/verify",
|
||||
"/api/stat/billserver/callback",
|
||||
"/api/stat/version/bottonclick",
|
||||
"/api/stat/version/bottonlist",
|
||||
"/api/stat/version/crossbottonclick",
|
||||
"/api/stat/share/list",
|
||||
"/api/stat/ads/get",
|
||||
"/api/stat/ads/click",
|
||||
"/api/stat/commodity/info",
|
||||
"/api/stat/commodity/rechargeType",
|
||||
"/api/stat/commodity/buy",
|
||||
"/api/stat/commodity/order",
|
||||
"/api/stat/version/app",
|
||||
"/api/stat/userInvite/userlist",
|
||||
"/api/stat/userInvite/incomelist",
|
||||
"/api/stat/userInvite/district",
|
||||
"/api/stat/user/districtcode",
|
||||
"/api/stat/callback/rchg/bill",
|
||||
"/api/stat/blocDailyStatTotal/list",
|
||||
"/api/stat/adminbloc/login",
|
||||
"/api/stat/channelCodeStat/list",
|
||||
"/api/stat/loufeng/sync",
|
||||
"/api/stat/districtMinuteStat/list",
|
||||
"/api/stat/loufeng/uploadImg",
|
||||
"/api/stat/loufeng/complaint", //楼凤投诉
|
||||
"/api/stat/model/sync", //嫩模同步
|
||||
"/api/stat/novel/sync",
|
||||
"/api/stat/novel/audio/sync",
|
||||
"/api/stat/file/upload",
|
||||
"/api/stat/model/syncBroker",
|
||||
"/api/stat/loufeng/complaint",
|
||||
"/api/stat/loufeng/queryComplaint",
|
||||
"/api/stat/openinstall/auth",
|
||||
"/api/stat/openinstall/total",
|
||||
"/api/stat/openinstall/info",
|
||||
"/api/stat/openinstall/growth",
|
||||
"/api/stat/openinstall/allstat",
|
||||
"/api/stat/openinstall/active",
|
||||
"/api/stat/activity/h5/info",
|
||||
"/api/stat/activity/h5/giftinfo",
|
||||
"/api/stat/activity/h5/draw",
|
||||
"/api/stat/landPageConfig/getByType",
|
||||
"/api/stat/media/m3u8",
|
||||
"/api/stat/media/enkey",
|
||||
"/api/stat/version/h5/config",
|
||||
"/api/stat/version/channel/newLink",
|
||||
"/api/stat/inner/botStatList",
|
||||
"/api/stat/inner/appList",
|
||||
"/api/stat/inner/appDownload",
|
||||
"/api/stat/inner/botMonthStat",
|
||||
"/api/stat/inner/sendTgMessage",
|
||||
"/api/stat/activity/visit",
|
||||
"/api/stat/landPage/summary/downTotal/list",
|
||||
"/api/stat/web/apps/get",
|
||||
"/api/stat/web/apps/click",
|
||||
"/api/stat/inner/recharge/list",
|
||||
"/api/stat/inner/rechargebotcmd/list",
|
||||
"/api/stat/daily-stat/jtspV2",
|
||||
"/api/stat/daily-stat/hour",
|
||||
"/api/stat/daily-stat/rank",
|
||||
"/api/stat/daily-stat/total",
|
||||
}
|
||||
)
|
||||
|
||||
func Auth(ctx *gin.Context) {
|
||||
for _, w := range whiteList {
|
||||
if strings.HasPrefix(ctx.Request.URL.Path, w) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
authToken := func() (int, error) {
|
||||
checkToken := ctx.Request.Header.Get("Authorization")
|
||||
if checkToken == "" {
|
||||
return http.StatusBadRequest, define.NoToken
|
||||
}
|
||||
claims, err := define.ParseToken(checkToken)
|
||||
if err != nil {
|
||||
return http.StatusMovedPermanently, define.RepeatLogin
|
||||
}
|
||||
|
||||
if claims.Type == define.Admin {
|
||||
has, err := adminMod.CheckToken(ctx, checkToken)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
if !has {
|
||||
return http.StatusMovedPermanently, define.RepeatLogin
|
||||
}
|
||||
} else if claims.Type == define.Channel {
|
||||
//fmt.Println("", claims.Type, claims.ShowMagic)
|
||||
if claims.ShowMagic > "3" {
|
||||
return consts.ShowMagic, nil
|
||||
}
|
||||
err := agentSer.CheckToken(ctx, claims.Name, checkToken)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
}
|
||||
ctx.Set(utils.AdminAccountKey, claims.Name)
|
||||
ctx.Set(utils.AccountTypeKey, claims.Type)
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
statusCode, err := authToken()
|
||||
if err != nil || statusCode != 0 {
|
||||
if statusCode == consts.ShowMagic {
|
||||
ctx.AbortWithStatusJSON(200, gin.H{})
|
||||
} else {
|
||||
ctx.AbortWithStatusJSON(statusCode, gin.H{"msg": fmt.Sprintf("%+v", err)})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package cors
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Origin *[]string //传入空指针,表示允许"*", 传入空数组表示禁止跨域
|
||||
Methods []string //Methods
|
||||
AllowedHeaders []string //AllowHeaders
|
||||
ExposedHeaders []string //exposeHeader
|
||||
Credentials bool //cookie
|
||||
MaxAge int64 //缓存MaxAge
|
||||
PreflightContinue bool //在遇到Options的时候,继续,而不是返回请求
|
||||
OptionsSuccessStatus int //Options时,返回的状态码,默认204
|
||||
}
|
||||
|
||||
func configureOrigin(o *Options, c *gin.Context) map[string]string {
|
||||
reqOrigin := c.GetHeader("Origin")
|
||||
headers := make(map[string]string)
|
||||
if o.Origin == nil {
|
||||
headers["Access-Control-Allow-Origin"] = "*"
|
||||
} else {
|
||||
isAllowed := false
|
||||
for _, allowed := range *o.Origin {
|
||||
if allowed == reqOrigin {
|
||||
isAllowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isAllowed {
|
||||
headers["Access-Control-Allow-Origin"] = reqOrigin
|
||||
} else {
|
||||
headers["Access-Control-Allow-Origin"] = strconv.FormatBool(false)
|
||||
}
|
||||
headers["Vary"] = "Origin"
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func configureMethods(o *Options) map[string]string {
|
||||
headers := make(map[string]string)
|
||||
if len(o.Methods) > 0 {
|
||||
headers["Access-Control-Allow-Methods"] = strings.ToUpper(strings.Join(o.Methods, ","))
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func configureCredentials(o *Options) map[string]string {
|
||||
headers := make(map[string]string)
|
||||
if o.Credentials {
|
||||
headers["Access-Control-Allow-Credentials"] = strconv.FormatBool(true)
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func configureAllowedHeaders(o *Options, c *gin.Context) map[string]string {
|
||||
headers := make(map[string]string)
|
||||
if len(o.AllowedHeaders) > 0 {
|
||||
headers["Access-Control-Allow-Headers"] = strings.Join(o.AllowedHeaders, ",")
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func configureExposedHeaders(o *Options) map[string]string {
|
||||
var headers = make(map[string]string)
|
||||
if len(o.ExposedHeaders) > 0 {
|
||||
headers["Access-Control-Expose-Headers"] = strings.Join(o.ExposedHeaders, ",")
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func configureMaxAge(o *Options) map[string]string {
|
||||
var headers = make(map[string]string)
|
||||
if o.MaxAge >= 0 {
|
||||
headers["Access-Control-Max-Age"] = strconv.FormatInt(o.MaxAge, 10)
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func mergeMap(m ...map[string]string) map[string]string {
|
||||
r := make(map[string]string)
|
||||
for _, t := range m {
|
||||
for k, v := range t {
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func applyHeader(c *gin.Context, h map[string]string) {
|
||||
for k, v := range h {
|
||||
c.Header(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
//Cors 跨域处理
|
||||
func Cors(o *Options) gin.HandlerFunc {
|
||||
if o.OptionsSuccessStatus == 0 {
|
||||
o.OptionsSuccessStatus = http.StatusNoContent
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
//c.Header("Access-Control-Allow-Origin", "*")
|
||||
//c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,Content-Length,Etag,Content-Range,Accept-Ranges,User-Agent,Range,Bucket,Content-Disposition,Signature,X-Forwarded-For,X-Real-Ip")
|
||||
if method == "OPTIONS" {
|
||||
h := mergeMap(
|
||||
configureOrigin(o, c),
|
||||
configureCredentials(o),
|
||||
configureMethods(o),
|
||||
configureAllowedHeaders(o, c),
|
||||
configureMaxAge(o),
|
||||
configureExposedHeaders(o),
|
||||
)
|
||||
applyHeader(c, h)
|
||||
if o.PreflightContinue {
|
||||
c.Next()
|
||||
} else {
|
||||
c.Header("Content-Length", "0")
|
||||
c.AbortWithStatus(o.OptionsSuccessStatus)
|
||||
}
|
||||
} else {
|
||||
h := mergeMap(
|
||||
configureOrigin(o, c),
|
||||
configureCredentials(o),
|
||||
configureExposedHeaders(o),
|
||||
)
|
||||
applyHeader(c, h)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package define
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"stat/statg"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ActType = string
|
||||
|
||||
const (
|
||||
Admin ActType = "admin"
|
||||
Channel ActType = "channel"
|
||||
District ActType = "district"
|
||||
ActTypeNone ActType = ""
|
||||
)
|
||||
|
||||
type Claims struct {
|
||||
Type ActType
|
||||
Name string `json:"name"`
|
||||
Role string `json:"role"`
|
||||
CID string `json:"cid"`
|
||||
ShowMagic string `json:"showMagic"`
|
||||
}
|
||||
|
||||
type claimsWithExp struct {
|
||||
Claims
|
||||
Exp int64 `json:"exp"`
|
||||
}
|
||||
|
||||
var (
|
||||
NoToken = fmt.Errorf("no token")
|
||||
RepeatLogin = fmt.Errorf("repeat login")
|
||||
)
|
||||
|
||||
const (
|
||||
WebTokenExpire = 10 * 24 * time.Hour
|
||||
)
|
||||
|
||||
func GetTokenSecret() string {
|
||||
return statg.Conf.JwtKey
|
||||
}
|
||||
|
||||
func GenToken(claims *Claims) (string, error) {
|
||||
c := claimsWithExp{Claims: *claims, Exp: time.Now().Add(WebTokenExpire).Unix()}
|
||||
secret := GetTokenSecret()
|
||||
args, _ := utils.JSONStruct2Map(c)
|
||||
token, err := utils.CreateToken(secret, args)
|
||||
if err != nil {
|
||||
log.Error("auth web GenToken error", log.Any("claims", claims), log.E(err))
|
||||
}
|
||||
return token, err
|
||||
}
|
||||
|
||||
func ParseToken(token string) (*claimsWithExp, error) {
|
||||
secret := GetTokenSecret()
|
||||
claims, err := utils.ParseToken(secret, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := claimsWithExp{}
|
||||
err = utils.Map2JSONStruct(&c, claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package inner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"stat/common/crypt"
|
||||
"stat/common/stdLog"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type cWriter struct {
|
||||
gin.ResponseWriter
|
||||
body *bytes.Buffer
|
||||
}
|
||||
|
||||
func (w *cWriter) Write(b []byte) (int, error) {
|
||||
return w.body.Write(b)
|
||||
}
|
||||
|
||||
func (w *cWriter) WriteString(s string) (int, error) {
|
||||
return w.body.WriteString(s)
|
||||
}
|
||||
|
||||
func Crypt(key string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// decrypt
|
||||
raw, err := c.GetRawData()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": stdLog.Failure,
|
||||
"msg": fmt.Sprintf("读取Body数据出错: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
de, err := crypt.CoreAesDecryptEx(raw, 12, key)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": stdLog.Failure,
|
||||
"msg": fmt.Sprintf("解密Body数据出错: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(de))
|
||||
c.Request.ContentLength = int64(len(de))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
oWriter := c.Writer
|
||||
blw := &cWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
|
||||
c.Writer = blw
|
||||
|
||||
c.Next()
|
||||
|
||||
// encrypt
|
||||
c.Writer = oWriter
|
||||
en, err := crypt.CoreAesEncryptEx(blw.body.Bytes(), 12, key)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": stdLog.Failure,
|
||||
"msg": fmt.Sprintf("加密Body数据出错: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = c.Writer.Write(en)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": stdLog.Failure,
|
||||
"msg": fmt.Sprintf("写入Body数据出错: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package limit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"stat/common/redis"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func GetKey(name string) func() string {
|
||||
return func() string {
|
||||
return redis.GetIpLimitDayKey(statg.Conf.Host, statg.Conf.Port) + "_" + name
|
||||
}
|
||||
}
|
||||
|
||||
func Limit(name string, capacity int64, rate int64) gin.HandlerFunc {
|
||||
limiter := redis.NewTokenBucket(redis.StatRds.Client(), GetKey(name), capacity, rate, 48*time.Hour)
|
||||
return func(ctx *gin.Context) {
|
||||
if err := limiter.TakeTokens(ctx.ClientIP(), 1); err != nil {
|
||||
ctx.AbortWithStatusJSON(200, gin.H{"msg": fmt.Sprintf("%+v", err)})
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user