dengbiao преди 5 дни
родител
ревизия
aefd2b198f
променени са 11 файла, в които са добавени 505 реда и са изтрити 123 реда
  1. +26
    -0
      app/lib/aes/check.go
  2. +84
    -0
      app/lib/aes/get_ip.go
  3. +6
    -0
      app/lib/aes/md/md_key.go
  4. +26
    -0
      app/lib/aes/sign.go
  5. +74
    -0
      app/lib/aes/utils.go
  6. +37
    -0
      app/mw/mw_check_sign.go
  7. +10
    -0
      app/svc/svc_common.go
  8. +72
    -0
      app/svc/svc_go_routine.go
  9. +100
    -0
      app/svc/svc_redis_mutex_lock.go
  10. +70
    -0
      app/svc/svc_user_invitecode.go
  11. +0
    -123
      app/utils/sign_check.go

+ 26
- 0
app/lib/aes/check.go Целия файл

@@ -0,0 +1,26 @@
package aes

import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
)

func CheckBody(request *http.Request, key string) {
body, _ := ioutil.ReadAll(request.Body)
if string(body) != "" {
fmt.Println("check_", string(body))
str := AesDecryptByECB(key, string(body))
if str != "" {
request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(str)))
}
}
return
}

func CheckError(err error) {
if err != nil {
panic(err)
}
}

+ 84
- 0
app/lib/aes/get_ip.go Целия файл

@@ -0,0 +1,84 @@
package aes

import (
"applet/app/cfg"
"applet/app/md"
"applet/app/utils"
"code.fnuoos.com/go_rely_warehouse/zyos_go_mq.git/rabbit"
"fmt"
"net"
"net/http"
"strings"
)

func GetIpAddress(request *http.Request, masterId, pvd string) {
//geoIp2db, _ := geoip2db.NewGeoipDbByStatik()
//defer geoIp2db.Close()
ip := GetRemoteClientIp(request)
if ip != "" {
//record, _ := geoIp2db.City(net.ParseIP(ip))
//if record.Country.Names != nil && record.Subdivisions != nil && record.City.Names != nil {

// aesData := fmt.Sprintf(
// `{"country":"%s","province":"%s","city":"%s","ip":"%s", "master_id":"%s", "url":"%s", "pvd":"%s"}`,
// record.Country.Names["zh-CN"], record.Subdivisions[0].Names["zh-CN"], record.City.Names["zh-CN"], ip, masterId, request.Host, pvd)

aesData := fmt.Sprintf(
`{"ip":"%s", "master_id":"%s", "url":"%s", "pvd":"%s"}`,
ip, masterId, request.Host, pvd)
fmt.Println(aesData)
if cfg.Prd {
//TODO::正式环境,推入rabbitMq
ch, _ := rabbit.Cfg.Pool.GetChannel()
defer ch.Release()
ch.Publish(md.UserVisitIpAddress, aesData, "")
}
//}
}

return
}

//func GetIp() (ip string) {
// conn, _ := net.Dial("udp", "google.com:80")
// if err != nil {
// return
// }
// defer conn.Close()
// ip = strings.Split(conn.LocalAddr().String(), ":")[0]
// return
//}

// GetRemoteClientIp 获取远程客户端IP
func GetRemoteClientIp(r *http.Request) string {
remoteIp := r.RemoteAddr
if ip := r.Header.Get("X-Real-IP"); ip != "" {
if strings.Contains(ip, "172.20") {
ips := strings.Split(r.Header.Get("X-Forwarded-For"), ",")
if len(ips) == 2 {
remoteIp = ips[0]
}
} else {
remoteIp = ip
}
} else if ip = r.Header.Get("X-Forwarded-For"); ip != "" {
remoteIp = ip
} else {
remoteIp, _, _ = net.SplitHostPort(remoteIp)
//remoteIp = ""
}

//本地ip
if remoteIp == "127.0.0.1" || remoteIp == "::1" {
remoteIp = "221.4.210.1651"
}

if r.Host == "h5.99813608.zhiyingos.com" {
utils.FilePutContents("GetRemoteClientIp", utils.SerializeStr(map[string]interface{}{
"header": r.Header,
"remoteIp": remoteIp,
}))
}
fmt.Println(">>>>>>>>>>>>>>>>>>>>GetRemoteClientIp<<<<<<<<<<<<<<<<<<", remoteIp)
return remoteIp
}

+ 6
- 0
app/lib/aes/md/md_key.go Целия файл

@@ -0,0 +1,6 @@
package md

const AesKey = "zhiyingos@qq.com"

const LimiterRequestIdPrefix = "request_domain_limiter_request_id:%s"
const LimiterLock = "request_domain_limiter_lock:%s" // 限流器锁

+ 26
- 0
app/lib/aes/sign.go Целия файл

@@ -0,0 +1,26 @@
package aes

import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
)

func CheckBody(request *http.Request, key string) {
body, _ := ioutil.ReadAll(request.Body)
if string(body) != "" {
fmt.Println("check_", string(body))
str := AesDecryptByECB(key, string(body))
if str != "" {
request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(str)))
}
}
return
}

func CheckError(err error) {
if err != nil {
panic(err)
}
}

+ 74
- 0
app/lib/aes/utils.go Целия файл

@@ -0,0 +1,74 @@
package aes

import (
"bytes"
"crypto/aes"
"encoding/base64"
)

// 加密
func AesEncryptByECB(key, data string) string {
// 判断key长度
keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}}
if _, ok := keyLenMap[len(key)]; !ok {
panic("key长度必须是 16、24、32 其中一个")
}
// 密钥和待加密数据转成[]byte
originByte := []byte(data)
keyByte := []byte(key)
// 创建密码组,长度只能是16、24、32 字节
block, _ := aes.NewCipher(keyByte)
// 获取密钥长度
blockSize := block.BlockSize()
// 补码
originByte = PKCS7Padding(originByte, blockSize)
// 创建保存加密变量
encryptResult := make([]byte, len(originByte))
// CEB是把整个明文分成若干段相同的小段,然后对每一小段进行加密
for bs, be := 0, blockSize; bs < len(originByte); bs, be = bs+blockSize, be+blockSize {
block.Encrypt(encryptResult[bs:be], originByte[bs:be])
}
res := base64.StdEncoding.EncodeToString(encryptResult)
return res
}

// 补码
func PKCS7Padding(originByte []byte, blockSize int) []byte {
// 计算补码长度
padding := blockSize - len(originByte)%blockSize
// 生成补码
padText := bytes.Repeat([]byte{byte(padding)}, padding)
// 追加补码
return append(originByte, padText...)
}

// 解密
func AesDecryptByECB(key, data string) string {
// 判断key长度
keyLenMap := map[int]struct{}{16: {}, 24: {}, 32: {}}
if _, ok := keyLenMap[len(key)]; !ok {
panic("key长度必须是 16、24、32 其中一个")
}
// 反解密码base64
originByte, _ := base64.StdEncoding.DecodeString(data)
// 密钥和待加密数据转成[]byte
keyByte := []byte(key)
// 创建密码组,长度只能是16、24、32字节
block, _ := aes.NewCipher(keyByte)
// 获取密钥长度
blockSize := block.BlockSize()
// 创建保存解密变量
decrypted := make([]byte, len(originByte))
for bs, be := 0, blockSize; bs < len(originByte); bs, be = bs+blockSize, be+blockSize {
block.Decrypt(decrypted[bs:be], originByte[bs:be])
}
// 解码
return string(PKCS7UNPadding(decrypted))
}

// 解码
func PKCS7UNPadding(originDataByte []byte) []byte {
length := len(originDataByte)
unpadding := int(originDataByte[length-1])
return originDataByte[:(length - unpadding)]
}

+ 37
- 0
app/mw/mw_check_sign.go Целия файл

@@ -0,0 +1,37 @@
package mw

import (
"applet/app/e"
"applet/app/utils"
"bytes"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"io/ioutil"
)

// CheckSign is 中间件 用来检查签名
func CheckSign(c *gin.Context) {
if utils.SignCheck(c) == false {
e.OutErr(c, 400, errors.New("请求失败~~"))
return
}
c.Next()
}

func CheckBody(c *gin.Context) {
c.Set("api_version", "1")
if utils.GetApiVersion(c) > 0 {
body, _ := ioutil.ReadAll(c.Request.Body)
fmt.Println("check_", c.GetString("mid"), string(body))
if string(body) != "" {
str := utils.ResultAesDecrypt(c, string(body))
fmt.Println("check_de", c.GetString("mid"), str)
if str != "" {
c.Set("body_str", str)
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(str)))
}
}
}
c.Next()
}

+ 10
- 0
app/svc/svc_common.go Целия файл

@@ -0,0 +1,10 @@
package svc

import "applet/app/utils/logx"

// 简单的recover
func Rev() {
if err := recover(); err != nil {
_ = logx.Error(err)
}
}

+ 72
- 0
app/svc/svc_go_routine.go Целия файл

@@ -0,0 +1,72 @@
package svc

import (
"applet/app/db"
"applet/app/db/model"
"applet/app/utils/logx"
"fmt"
"time"
)

// RoutineInsertUserRelate is 协程 当关联上级用户时,需要查询当前用户的所有关联下级,并新增关联上级与当前用户下级关系
func RoutineInsertUserRelate(puid, uid, addlv int) {
defer Rev()
urs, err := db.UserRelatesByPuid(db.Db, uid, 0, 0)
if err != nil {
logx.Warn(err)
}
// fmt.Println(*urs)
for _, item := range *urs {
_, err = db.UserRelateInsert(db.Db,
&model.UserRelate{
ParentUid: puid,
Uid: item.Uid,
Level: item.Level + addlv,
InviteTime: time.Now(),
})
if err != nil {
continue
}
logx.Info(fmt.Sprintf("关联pid(%v) -> uid(%v) ,lv:%v", puid, item.Uid, item.Level+addlv))

}
}

// RoutineMultiRelate is 多级关联
func RoutineMultiRelate(pid int, uid int, lv int) {
userDb := db.UserDb{}
userDb.Set()

defer Rev()
for {
if pid == 0 {
break
}
m, err := userDb.GetUser(pid)
if err != nil || m == nil {
logx.Warn(err)
break
}
if m.ParentUid == 0 {
break
}
lv++
ur := new(model.UserRelate)
ur.ParentUid = m.ParentUid
ur.Uid = uid
ur.Level = lv
ur.InviteTime = time.Now()
_, err = db.UserRelateInsert(db.Db, ur)
if err != nil {
logx.Warn(err)
break
}
// 还要关联当前的用户的所有下级,注意关联等级
RoutineInsertUserRelate(m.ParentUid, uid, lv)
// 下级关联上上级
// 继续查询
logx.Info(fmt.Sprintf("关联pid(%v) -> uid(%v),lv:%v", ur.ParentUid, ur.Uid, lv))
logx.Info("继续查询")
pid = m.ParentUid
}
}

+ 100
- 0
app/svc/svc_redis_mutex_lock.go Целия файл

@@ -0,0 +1,100 @@
package svc

import (
"applet/app/md"
"applet/app/utils"
"applet/app/utils/cache"
"errors"
"fmt"
"math/rand"
"reflect"
"time"
)

const redisMutexLockExpTime = 15

// TryGetDistributedLock 分布式锁获取
// requestId 用于标识请求客户端,可以是随机字符串,需确保唯一
func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool {
if isNegative { // 多次尝试获取
retry := 1
for {
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
// 获取锁成功
if err == nil && ok == "OK" {
return true
}
// 尝试多次没获取成功
if retry > 10 {
return false
}
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
retry += 1
}
} else { // 只尝试一次
ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
// 获取锁成功
if err == nil && ok == "OK" {
return true
}

return false
}
}

// ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型
func ReleaseDistributedLock(lockKey, requestId string) (bool, error) {
luaScript := `
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end`

do, err := cache.Do("eval", luaScript, 1, lockKey, requestId)
fmt.Println(reflect.TypeOf(do))
fmt.Println(do)

if utils.AnyToInt64(do) == 1 {
return true, err
} else {
return false, err
}
}

func GetDistributedLockRequestId(prefix string) string {
return prefix + utils.IntToStr(rand.Intn(100000000))
}

// HandleBalanceDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功,使用语句 ` defer cb() ` 释放锁
func HandleBalanceDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) {
// 获取余额更新锁
balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid)
requestId := GetDistributedLockRequestId(requestIdPrefix)
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
if !balanceLockOk {
return nil, errors.New("系统繁忙,请稍后再试")
}

cb = func() {
_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
}

return cb, nil
}

func HandleLimiterDistributedLock(masterId, ip, requestIdPrefix string) (cb func(), err error) {
balanceLockKey := fmt.Sprintf(md.AppLimiterLock, masterId, ip)
requestId := GetDistributedLockRequestId(requestIdPrefix)
balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
if !balanceLockOk {
return nil, errors.New("系统繁忙,请稍后再试")
}

cb = func() {
_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
}

return cb, nil
}

+ 70
- 0
app/svc/svc_user_invitecode.go Целия файл

@@ -0,0 +1,70 @@
package svc

import (
"applet/app/db"
"math/rand"
"unicode"
)

func ReturnCode(l, types, num int) string {
if num > 5 {
return ""
}
//循环3次判断是否存在该邀请码
var code string
var (
codes []string
)
for i := 0; i < 3; i++ {
oneCode := GetRandomString(l, types)
codes = append(codes, oneCode)
}

//判断是不是存在邀请码了
tmp, _ := db.UserProfileFindByInviteCodes(db.Db, codes...)

//循环生成的邀请码 判断tmp里有没有这个邀请码 如果邀请码没有就赋值 再判断是否存在 存在就清空
for _, v := range codes {
if code != "" { //如果存在并且数据库没有就跳过
continue
}
code = v
for _, v1 := range *tmp {
//如果存在就清空
if v1.InviteCode == v {
code = ""
}
}
}
//如果都没有就继续加一位继续查
if code == "" {
return ReturnCode(l+1, types, num+1)
}
return code
}

// 随机生成指定位数的大写字母和数字的组合
func GetRandomString(l, isLetter int) string {
str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
if isLetter != 1 {
str = "0123456789"
}
strs := []rune(str)
result := make([]rune, l)
for i := range result {
result[i] = strs[rand.Intn(len(strs))]
}
if IsLetter(string(result)) && isLetter == 1 {
return GetRandomString(l, isLetter)
}
return string(result)
}

func IsLetter(s string) bool {
for _, r := range s {
if !unicode.IsLetter(r) {
return false
}
}
return true
}

+ 0
- 123
app/utils/sign_check.go Целия файл

@@ -1,125 +0,0 @@
package utils

import (
"applet/app/utils/logx"
"fmt"
"github.com/forgoer/openssl"
"github.com/gin-gonic/gin"
"github.com/syyongx/php2go"
"strings"
)

var publicKey = []byte(`-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCFQD7RL2tDNuwdg0jTfV0zjAzh
WoCWfGrcNiucy2XUHZZU2oGhHv1N10qu3XayTDD4pu4sJ73biKwqR6ZN7IS4Sfon
vrzaXGvrTG4kmdo3XrbrkzmyBHDLTsJvv6pyS2HPl9QPSvKDN0iJ66+KN8QjBpw1
FNIGe7xbDaJPY733/QIDAQAB

var privateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCFQD7RL2tDNuwdg0jTfV0zjAzhWoCWfGrcNiucy2XUHZZU2oGh
Hv1N10qu3XayTDD4pu4sJ73biKwqR6ZN7IS4SfonvrzaXGvrTG4kmdo3Xrbrkzmy
BHDLTsJvv6pyS2HPl9QPSvKDN0iJ66+KN8QjBpw1FNIGe7xbDaJPY733/QIDAQAB
AoGADi14wY8XDY7Bbp5yWDZFfV+QW0Xi2qAgSo/k8gjeK8R+I0cgdcEzWF3oz1Q2
9d+PclVokAAmfj47e0AmXLImqMCSEzi1jDBUFIRoJk9WE1YstE94mrCgV0FW+N/u
+L6OgZcjmF+9dHKprnpaUGQuUV5fF8j0qp8S2Jfs3Sw+dOECQQCQnHALzFjmXXIR
Ez3VSK4ZoYgDIrrpzNst5Hh6AMDNZcG3CrCxlQrgqjgTzBSr3ZSavvkfYRj42STk
TqyX1tQFAkEA6+O6UENoUTk2lG7iO/ta7cdIULnkTGwQqvkgLIUjk6w8E3sBTIfw
rerTEmquw5F42HHE+FMrRat06ZN57lENmQJAYgUHlZevcoZIePZ35Qfcqpbo4Gc8
Fpm6vwKr/tZf2Vlt0qo2VkhWFS6L0C92m4AX6EQmDHT+Pj7BWNdS+aCuGQJBAOkq
NKPZvWdr8jNOV3mKvxqB/U0uMigIOYGGtvLKt5vkh42J7ILFbHW8w95UbWMKjDUG
X/hF3WQEUo//Imsa2yECQHSZIpJxiTRueoDiyRt0LH+jdbYFUu/6D0UIYXhFvP/p
EZX+hfCfUnNYX59UVpRjSZ66g0CbCjuBPOhmOD+hDeQ=

func GetApiVersion(c *gin.Context) int {
var apiVersion = c.GetHeader("apiVersion")
if StrToInt(apiVersion) == 0 { //没有版本号先不校验
apiVersion = c.GetHeader("Apiversion")
}
if StrToInt(apiVersion) == 0 { //没有版本号先不校验
apiVersion = c.GetHeader("api_version")
}
return StrToInt(apiVersion)
}

//签名校验
func SignCheck(c *gin.Context) bool {
var apiVersion = GetApiVersion(c)
if apiVersion == 0 { //没有版本号先不校验
return true
}
//1.通过rsa 解析出 aes
var key = c.GetHeader("key")

//拼接对应参数
var uri = c.Request.RequestURI
var query = GetQueryParam(uri)
fmt.Println(query)
query["timestamp"] = c.GetHeader("timestamp")
query["nonce"] = c.GetHeader("nonce")
query["key"] = key
token := c.GetHeader("Authorization")
if token != "" {
// 按空格分割
parts := strings.SplitN(token, " ", 2)
if len(parts) == 2 && parts[0] == "Bearer" {
token = parts[1]
}
}
query["token"] = token
//2.query参数按照 ASCII 码从小到大排序
str := JoinStringsInASCII(query, "&", false, false, "")
//3.拼上密钥
secret := ""
if InArr(c.GetHeader("platform"), []string{"android", "ios"}) {
secret = c.GetString("app_api_secret_key")
} else if c.GetHeader("platform") == "wap" {
secret = c.GetString("h5_api_secret_key")
} else {
secret = c.GetString("applet_api_secret_key")
}
str = fmt.Sprintf("%s&secret=%s", str, secret)
fmt.Println(str)
//4.md5加密 转小写
sign := strings.ToLower(Md5(str))
//5.判断跟前端传来的sign是否一致
if sign != c.GetHeader("sign") {
return false
}
return true
}

func ResultAes(c *gin.Context, raw []byte) string {
var key = c.GetHeader("key")
base, _ := php2go.Base64Decode(key)
aes, err := RsaDecrypt([]byte(base), privateKey)
if err != nil {
logx.Info(err)
return ""
}

str, _ := openssl.AesECBEncrypt(raw, aes, openssl.PKCS7_PADDING)
value := php2go.Base64Encode(string(str))
fmt.Println(value)

return value
}

func ResultAesDecrypt(c *gin.Context, raw string) string {
var key = c.GetHeader("key")
base, _ := php2go.Base64Decode(key)
aes, err := RsaDecrypt([]byte(base), privateKey)
if err != nil {
logx.Info(err)
return ""
}
fmt.Println(raw)
value1, _ := php2go.Base64Decode(raw)
if value1 == "" {
return ""
}
str1, _ := openssl.AesECBDecrypt([]byte(value1), aes, openssl.PKCS7_PADDING)

return string(str1)
}

Зареждане…
Отказ
Запис