commit
This commit is contained in:
17
pkg/cache/manager.go
vendored
Normal file
17
pkg/cache/manager.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package cache
|
||||
|
||||
// Manager 管理缓存的接口定义
|
||||
type Manager interface {
|
||||
// Set 设置缓存
|
||||
Set(key string, value interface{}) error
|
||||
// SetDefault 设置缓存
|
||||
SetDefault(key string, value interface{}, expire int64) error
|
||||
// Get 获取缓存
|
||||
Get(key string) (interface{}, error)
|
||||
// GetString 获取缓存
|
||||
GetString(key string) (string, error)
|
||||
// Exists key是否存在
|
||||
Exists(key string) bool
|
||||
// Delete 删除key
|
||||
Delete(key string) error
|
||||
}
|
||||
70
pkg/cache/memory_manager.go
vendored
Normal file
70
pkg/cache/memory_manager.go
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
// MemoryManager memory缓存管理
|
||||
type MemoryManager struct {
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
// NewMemoryManager 获取一个新的内存管理对象
|
||||
// defaultExpiration 缓存过期时间
|
||||
// cleanupInterval 缓存清理时间
|
||||
func NewMemoryManager(defaultExpiration, cleanupInterval time.Duration) Manager {
|
||||
return &MemoryManager{cache: cache.New(defaultExpiration, cleanupInterval)}
|
||||
}
|
||||
|
||||
// Set 设置缓存
|
||||
// key 键
|
||||
// value 值
|
||||
func (manager *MemoryManager) Set(key string, value interface{}) error {
|
||||
manager.cache.Set(key, value, time.Hour*24*365*100) // 10年
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefault 设置缓存
|
||||
// key 键
|
||||
// value 值
|
||||
// expire 过期时间 (秒)
|
||||
func (manager *MemoryManager) SetDefault(key string, value interface{}, expire int64) error {
|
||||
manager.cache.Set(key, value, time.Second*time.Duration(expire))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取缓存
|
||||
func (manager *MemoryManager) Get(key string) (interface{}, error) {
|
||||
value, found := manager.cache.Get(key)
|
||||
if found {
|
||||
return value, nil
|
||||
}
|
||||
return nil, errors.New("no found data with key")
|
||||
}
|
||||
|
||||
// GetString 获取缓存
|
||||
func (manager *MemoryManager) GetString(key string) (string, error) {
|
||||
value, found := manager.cache.Get(key)
|
||||
if found {
|
||||
return fmt.Sprintf("%v", value), nil
|
||||
}
|
||||
return "", errors.New("no found data with key")
|
||||
}
|
||||
|
||||
// Exists key是否存在
|
||||
func (manager *MemoryManager) Exists(key string) bool {
|
||||
if _, found := manager.cache.Get(key); found {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete 删除key
|
||||
func (manager *MemoryManager) Delete(key string) error {
|
||||
manager.cache.Delete(key)
|
||||
return nil
|
||||
}
|
||||
135
pkg/cache/redis_manager.go
vendored
Normal file
135
pkg/cache/redis_manager.go
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
// RedisManager redis缓存管理
|
||||
type RedisManager struct {
|
||||
pool *redis.Pool
|
||||
}
|
||||
|
||||
// NewRedisManager 获取一个新的redis管理对象
|
||||
func NewRedisManager(address string) Manager {
|
||||
pool := &redis.Pool{
|
||||
// 连接方法
|
||||
Dial: func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Do("SELECT", 0)
|
||||
return c, nil
|
||||
},
|
||||
//DialContext: nil,
|
||||
//TestOnBorrow: nil,
|
||||
MaxIdle: 10, // 最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态。
|
||||
MaxActive: 10, // 最大的激活连接数,表示同时最多有N个连接
|
||||
IdleTimeout: 360 * time.Second, // 最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
|
||||
//Wait: false,
|
||||
//MaxConnLifetime: 0,
|
||||
}
|
||||
return &RedisManager{pool}
|
||||
}
|
||||
|
||||
// Set 设置缓存
|
||||
// key 键
|
||||
// value 值
|
||||
func (manager *RedisManager) Set(key string, value interface{}) error {
|
||||
conn := manager.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
_, err := conn.Do("Set", key, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set cache error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefault 设置缓存
|
||||
// key 键
|
||||
// value 值
|
||||
// expire 过期时间 (秒)
|
||||
func (manager *RedisManager) SetDefault(key string, value interface{}, expire int64) error {
|
||||
conn := manager.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
//_, err := conn.Do("set", key, value)
|
||||
//if err != nil {
|
||||
// return fmt.Errorf("set default cache error: %v", err)
|
||||
//}
|
||||
//
|
||||
//// 设置过期时间
|
||||
//conn.Do("expire", key, expire)
|
||||
//return nil
|
||||
|
||||
_, err := conn.Do("set", key, value, "ex", expire)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set default cache error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取缓存
|
||||
func (manager *RedisManager) Get(key string) (interface{}, error) {
|
||||
conn := manager.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
// 检查key是否存在
|
||||
exit, err := redis.Bool(conn.Do("exists", key))
|
||||
if err != nil || !exit {
|
||||
return nil, fmt.Errorf("key is not exists")
|
||||
}
|
||||
|
||||
value, err := conn.Do("get", key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get cache error: %v", err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// GetString 获取缓存
|
||||
func (manager *RedisManager) GetString(key string) (string, error) {
|
||||
conn := manager.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
// 检查key是否存在
|
||||
exit, err := redis.Bool(conn.Do("exists", key))
|
||||
if err != nil || !exit {
|
||||
return "", fmt.Errorf("key is not exists")
|
||||
}
|
||||
|
||||
value, err := redis.String(conn.Do("get", key))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get cache error: %v", err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Exists key是否存在
|
||||
func (manager *RedisManager) Exists(key string) bool {
|
||||
conn := manager.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
// 检查key是否存在
|
||||
exit, err := redis.Bool(conn.Do("expire", key))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exit
|
||||
}
|
||||
|
||||
// Delete 删除key
|
||||
func (manager *RedisManager) Delete(key string) error {
|
||||
conn := manager.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
_, err := conn.Do("del", key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除key:[%s]异常:%v", key, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
349
pkg/ip2region/ip2Region.go
Normal file
349
pkg/ip2region/ip2Region.go
Normal file
@@ -0,0 +1,349 @@
|
||||
package ip2region
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
INDEX_BLOCK_LENGTH = 12
|
||||
TOTAL_HEADER_LENGTH = 8192
|
||||
)
|
||||
|
||||
var err error
|
||||
var ipInfo IpInfo
|
||||
|
||||
type Ip2Region struct {
|
||||
// db file handler
|
||||
dbFileHandler *os.File
|
||||
|
||||
|
||||
//header block info
|
||||
|
||||
headerSip []int64
|
||||
headerPtr []int64
|
||||
headerLen int64
|
||||
|
||||
// super block index info
|
||||
firstIndexPtr int64
|
||||
lastIndexPtr int64
|
||||
totalBlocks int64
|
||||
|
||||
// for memory mode only
|
||||
// the original db binary string
|
||||
|
||||
dbBinStr []byte
|
||||
dbFile string
|
||||
}
|
||||
|
||||
type IpInfo struct {
|
||||
CityId int64
|
||||
Country string
|
||||
Region string
|
||||
Province string
|
||||
City string
|
||||
ISP string
|
||||
}
|
||||
|
||||
func (ip IpInfo)String() string {
|
||||
return strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
|
||||
}
|
||||
|
||||
func getIpInfo(cityId int64, line []byte) IpInfo {
|
||||
|
||||
lineSlice := strings.Split(string(line), "|")
|
||||
ipInfo := IpInfo{}
|
||||
length := len(lineSlice)
|
||||
ipInfo.CityId = cityId
|
||||
if length < 5 {
|
||||
for i := 0; i <= 5 - length; i++ {
|
||||
lineSlice = append(lineSlice, "")
|
||||
}
|
||||
}
|
||||
|
||||
ipInfo.Country = lineSlice[0]
|
||||
ipInfo.Region = lineSlice[1]
|
||||
ipInfo.Province = lineSlice[2]
|
||||
ipInfo.City = lineSlice[3]
|
||||
ipInfo.ISP = lineSlice[4]
|
||||
return ipInfo
|
||||
}
|
||||
|
||||
func New(path string) (*Ip2Region, error) {
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Ip2Region{
|
||||
dbFile:path,
|
||||
dbFileHandler:file,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *Ip2Region) Close() {
|
||||
this.dbFileHandler.Close()
|
||||
}
|
||||
|
||||
func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
|
||||
ipInfo = IpInfo{}
|
||||
|
||||
if this.totalBlocks == 0 {
|
||||
this.dbBinStr, err = ioutil.ReadFile(this.dbFile)
|
||||
|
||||
if err != nil {
|
||||
|
||||
return ipInfo, err
|
||||
}
|
||||
|
||||
this.firstIndexPtr = getLong(this.dbBinStr, 0)
|
||||
this.lastIndexPtr = getLong(this.dbBinStr, 4)
|
||||
this.totalBlocks = (this.lastIndexPtr - this.firstIndexPtr) /INDEX_BLOCK_LENGTH + 1
|
||||
}
|
||||
|
||||
ip, err := ip2long(ipStr)
|
||||
if err != nil {
|
||||
return ipInfo, err
|
||||
}
|
||||
|
||||
h := this.totalBlocks
|
||||
var dataPtr, l int64;
|
||||
for (l <= h) {
|
||||
|
||||
m := (l + h) >> 1
|
||||
p := this.firstIndexPtr + m *INDEX_BLOCK_LENGTH
|
||||
sip := getLong(this.dbBinStr, p)
|
||||
if ip < sip {
|
||||
h = m - 1
|
||||
} else {
|
||||
eip := getLong(this.dbBinStr, p + 4)
|
||||
if ip > eip {
|
||||
l = m + 1
|
||||
} else {
|
||||
dataPtr = getLong(this.dbBinStr, p + 8)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if dataPtr == 0 {
|
||||
return ipInfo, errors.New("not found")
|
||||
}
|
||||
|
||||
dataLen := ((dataPtr >> 24) & 0xFF)
|
||||
dataPtr = (dataPtr & 0x00FFFFFF);
|
||||
ipInfo = getIpInfo(getLong(this.dbBinStr, dataPtr), this.dbBinStr[(dataPtr) + 4:dataPtr + dataLen])
|
||||
return ipInfo, nil
|
||||
|
||||
}
|
||||
|
||||
func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
|
||||
ipInfo = IpInfo{}
|
||||
if this.totalBlocks == 0 {
|
||||
this.dbFileHandler.Seek(0, 0)
|
||||
superBlock := make([]byte, 8)
|
||||
this.dbFileHandler.Read(superBlock)
|
||||
this.firstIndexPtr = getLong(superBlock, 0)
|
||||
this.lastIndexPtr = getLong(superBlock, 4)
|
||||
this.totalBlocks = (this.lastIndexPtr - this.firstIndexPtr) /INDEX_BLOCK_LENGTH + 1
|
||||
}
|
||||
|
||||
var l, dataPtr, p int64
|
||||
|
||||
h := this.totalBlocks
|
||||
|
||||
ip, err := ip2long(ipStr)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for (l <= h) {
|
||||
m := (l + h) >> 1
|
||||
|
||||
p = m * INDEX_BLOCK_LENGTH
|
||||
|
||||
_, err = this.dbFileHandler.Seek(this.firstIndexPtr + p, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buffer := make([]byte, INDEX_BLOCK_LENGTH)
|
||||
_, err = this.dbFileHandler.Read(buffer)
|
||||
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
sip := getLong(buffer, 0)
|
||||
if ip < sip {
|
||||
h = m - 1
|
||||
} else {
|
||||
eip := getLong(buffer, 4)
|
||||
if ip > eip {
|
||||
l = m + 1
|
||||
} else {
|
||||
dataPtr = getLong(buffer, 8)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if dataPtr == 0 {
|
||||
err = errors.New("not found")
|
||||
return
|
||||
}
|
||||
|
||||
dataLen := ((dataPtr >> 24) & 0xFF)
|
||||
dataPtr = (dataPtr & 0x00FFFFFF);
|
||||
|
||||
this.dbFileHandler.Seek(dataPtr, 0)
|
||||
data := make([]byte, dataLen)
|
||||
this.dbFileHandler.Read(data)
|
||||
ipInfo = getIpInfo(getLong(data, 0), data[4:dataLen])
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
|
||||
ipInfo = IpInfo{}
|
||||
ip, err := ip2long(ipStr)
|
||||
|
||||
if this.headerLen == 0 {
|
||||
this.dbFileHandler.Seek(8, 0)
|
||||
|
||||
buffer := make([]byte, TOTAL_HEADER_LENGTH)
|
||||
this.dbFileHandler.Read(buffer)
|
||||
var idx int64;
|
||||
for i := 0; i < TOTAL_HEADER_LENGTH; i += 8 {
|
||||
startIp := getLong(buffer, int64(i))
|
||||
dataPar := getLong(buffer, int64(i + 4))
|
||||
if dataPar == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
this.headerSip = append(this.headerSip, startIp)
|
||||
this.headerPtr = append(this.headerPtr, dataPar)
|
||||
idx ++;
|
||||
}
|
||||
|
||||
this.headerLen = idx
|
||||
}
|
||||
|
||||
var l, sptr, eptr int64
|
||||
h := this.headerLen
|
||||
|
||||
for l <= h {
|
||||
m := int64(l + h) >> 1
|
||||
if m < this.headerLen {
|
||||
if ip == this.headerSip[m] {
|
||||
if m > 0 {
|
||||
sptr = this.headerPtr[m - 1]
|
||||
eptr = this.headerPtr[m]
|
||||
} else {
|
||||
sptr = this.headerPtr[m]
|
||||
eptr = this.headerPtr[m + 1]
|
||||
}
|
||||
break
|
||||
}
|
||||
if ip < this.headerSip[m] {
|
||||
if m == 0 {
|
||||
sptr = this.headerPtr[m]
|
||||
eptr = this.headerPtr[m + 1]
|
||||
break
|
||||
} else if ip > this.headerSip[m - 1] {
|
||||
sptr = this.headerPtr[m - 1]
|
||||
eptr = this.headerPtr[m]
|
||||
break
|
||||
}
|
||||
h = m - 1
|
||||
} else {
|
||||
if m == this.headerLen - 1 {
|
||||
sptr = this.headerPtr[m - 1]
|
||||
eptr = this.headerPtr[m]
|
||||
break
|
||||
} else if ip <= this.headerSip[m + 1] {
|
||||
sptr = this.headerPtr[m ]
|
||||
eptr = this.headerPtr[m + 1]
|
||||
break
|
||||
}
|
||||
l = m + 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if sptr == 0 {
|
||||
err = errors.New("not found")
|
||||
return
|
||||
}
|
||||
|
||||
blockLen := eptr - sptr
|
||||
this.dbFileHandler.Seek(sptr, 0)
|
||||
index := make([]byte, blockLen +INDEX_BLOCK_LENGTH)
|
||||
this.dbFileHandler.Read(index)
|
||||
var dataptr int64
|
||||
h = blockLen / INDEX_BLOCK_LENGTH
|
||||
l = 0
|
||||
|
||||
for l <= h {
|
||||
m := int64(l + h) >> 1
|
||||
p := m * INDEX_BLOCK_LENGTH
|
||||
sip := getLong(index, p)
|
||||
if ip < sip {
|
||||
h = m - 1;
|
||||
} else {
|
||||
eip := getLong(index, p + 4)
|
||||
if ip > eip {
|
||||
l = m + 1
|
||||
} else {
|
||||
dataptr = getLong(index, p + 8)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dataptr == 0 {
|
||||
err = errors.New("not found")
|
||||
return
|
||||
}
|
||||
|
||||
dataLen := (dataptr >> 24) & 0xFF
|
||||
dataPtr := dataptr & 0x00FFFFFF
|
||||
|
||||
this.dbFileHandler.Seek(dataPtr, 0)
|
||||
data := make([]byte, dataLen)
|
||||
this.dbFileHandler.Read(data)
|
||||
ipInfo = getIpInfo(getLong(data, 0), data[4:])
|
||||
return
|
||||
}
|
||||
|
||||
func getLong(b []byte, offset int64) int64 {
|
||||
|
||||
val := (int64(b[offset ]) |
|
||||
int64(b[offset + 1]) << 8 |
|
||||
int64(b[offset + 2]) << 16 |
|
||||
int64(b[offset + 3]) << 24)
|
||||
|
||||
return val
|
||||
|
||||
}
|
||||
|
||||
func ip2long(IpStr string) (int64, error) {
|
||||
bits := strings.Split(IpStr, ".")
|
||||
if len(bits) != 4 {
|
||||
return 0, errors.New("ip format error")
|
||||
}
|
||||
|
||||
var sum int64
|
||||
for i, n := range bits {
|
||||
bit, _ := strconv.ParseInt(n, 10, 64)
|
||||
sum += bit << uint(24 - 8 * i)
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
49
pkg/ip2region/ip2Region_test.go
Normal file
49
pkg/ip2region/ip2Region_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package ip2region
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkBtreeSearch(B *testing.B) {
|
||||
region, err := New("../../data/ip2region.db ")
|
||||
if err != nil {
|
||||
B.Error(err)
|
||||
}
|
||||
for i:=0;i<B.N;i++{
|
||||
region.BtreeSearch("127.0.0.1")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkMemorySearch(B *testing.B) {
|
||||
region, err := New("../../data/ip2region.db ")
|
||||
if err != nil {
|
||||
B.Error(err)
|
||||
}
|
||||
for i:=0;i<B.N;i++{
|
||||
region.MemorySearch("127.0.0.1")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkBinarySearch(B *testing.B) {
|
||||
region, err := New("../../data/ip2region.db ")
|
||||
if err != nil {
|
||||
B.Error(err)
|
||||
}
|
||||
for i:=0;i<B.N;i++{
|
||||
region.BinarySearch("127.0.0.1")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIp2long(t *testing.T) {
|
||||
ip, err := ip2long("127.0.0.1")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if ip != 2130706433 {
|
||||
t.Error("result error")
|
||||
}
|
||||
t.Log(ip)
|
||||
}
|
||||
62
pkg/token/jwt_maker.go
Normal file
62
pkg/token/jwt_maker.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
const minSecretKeySize = 32
|
||||
|
||||
// JWTMaker JSON Web Token
|
||||
type JWTMaker struct {
|
||||
secretKey string
|
||||
}
|
||||
|
||||
// NewJWTMaker 创建一个新的JWTMaker
|
||||
func NewJWTMaker(secretKey string) (Maker, error) {
|
||||
if len(secretKey) < minSecretKeySize {
|
||||
return nil, fmt.Errorf("invalid key size: must be at least %d characters", minSecretKeySize)
|
||||
}
|
||||
return &JWTMaker{secretKey}, nil
|
||||
}
|
||||
|
||||
// CreateToken 根据用户名和时间创建一个新的token
|
||||
func (maker *JWTMaker) CreateToken(username string, duration time.Duration) (string, error) {
|
||||
payload, err := NewPayload(username, duration)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
|
||||
return jwtToken.SignedString([]byte(maker.secretKey))
|
||||
}
|
||||
|
||||
// VerifyToken checks if the token is valid or not
|
||||
func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) {
|
||||
keyFunc := func(token *jwt.Token) (interface{}, error) {
|
||||
_, ok := token.Method.(*jwt.SigningMethodHMAC)
|
||||
if !ok {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
return []byte(maker.secretKey), nil
|
||||
}
|
||||
|
||||
jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc)
|
||||
if err != nil {
|
||||
verr, ok := err.(*jwt.ValidationError)
|
||||
if ok && errors.Is(verr.Inner, ErrExpiredToken) {
|
||||
return nil, ErrExpiredToken
|
||||
}
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
payload, ok := jwtToken.Claims.(*Payload)
|
||||
if !ok {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
14
pkg/token/maker.go
Normal file
14
pkg/token/maker.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Maker 管理token的接口定义
|
||||
type Maker interface {
|
||||
// CreateToken 根据用户名和时间创建一个新的token
|
||||
CreateToken(username string, duration time.Duration) (string, error)
|
||||
|
||||
// VerifyToken 校验token是否正确
|
||||
VerifyToken(token string) (*Payload, error)
|
||||
}
|
||||
56
pkg/token/paseto_maker.go
Normal file
56
pkg/token/paseto_maker.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aead/chacha20poly1305"
|
||||
"github.com/o1egl/paseto"
|
||||
)
|
||||
|
||||
// PasetoMaker is a PASETO token maker
|
||||
type PasetoMaker struct {
|
||||
paseto *paseto.V2
|
||||
symmetricKey []byte
|
||||
}
|
||||
|
||||
// NewPasetoMaker creates a new PasetoMaker
|
||||
func NewPasetoMaker(symmetricKey string) (Maker, error) {
|
||||
if len(symmetricKey) != chacha20poly1305.KeySize {
|
||||
return nil, fmt.Errorf("invalid key size: must be exactly %d characters", chacha20poly1305.KeySize)
|
||||
}
|
||||
|
||||
maker := &PasetoMaker{
|
||||
paseto: paseto.NewV2(),
|
||||
symmetricKey: []byte(symmetricKey),
|
||||
}
|
||||
|
||||
return maker, nil
|
||||
}
|
||||
|
||||
// CreateToken creates a new token for a specific username and duration
|
||||
func (maker *PasetoMaker) CreateToken(username string, duration time.Duration) (string, error) {
|
||||
payload, err := NewPayload(username, duration)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return maker.paseto.Encrypt(maker.symmetricKey, payload, nil)
|
||||
}
|
||||
|
||||
// VerifyToken checks if the token is valid or not
|
||||
func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) {
|
||||
payload := &Payload{}
|
||||
|
||||
err := maker.paseto.Decrypt(token, maker.symmetricKey, payload, nil)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
err = payload.Valid()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
46
pkg/token/payload.go
Normal file
46
pkg/token/payload.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Different types of error returned by the VerifyToken function
|
||||
var (
|
||||
ErrInvalidToken = errors.New("token is invalid")
|
||||
ErrExpiredToken = errors.New("token has expired")
|
||||
)
|
||||
|
||||
// Payload contains the payload data of the token
|
||||
type Payload struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Username string `json:"username"`
|
||||
IssuedAt time.Time `json:"issued_at"`
|
||||
ExpiredAt time.Time `json:"expired_at"`
|
||||
}
|
||||
|
||||
// NewPayload creates a new token payload with a specific username and duration
|
||||
func NewPayload(username string, duration time.Duration) (*Payload, error) {
|
||||
tokenID, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload := &Payload{
|
||||
ID: tokenID,
|
||||
Username: username,
|
||||
IssuedAt: time.Now(),
|
||||
ExpiredAt: time.Now().Add(duration),
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// Valid checks if the token payload is valid or not
|
||||
func (payload *Payload) Valid() error {
|
||||
if time.Now().After(payload.ExpiredAt) {
|
||||
return ErrExpiredToken
|
||||
}
|
||||
return nil
|
||||
}
|
||||
33
pkg/util/cache.go
Normal file
33
pkg/util/cache.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package util
|
||||
|
||||
// 简单缓存 适合小数据量
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var globalMap sync.Map
|
||||
var cacheLen int64
|
||||
|
||||
func SetCache(key string, data interface{}, timeout int) {
|
||||
globalMap.Store(key, data)
|
||||
atomic.AddInt64(&cacheLen, 1)
|
||||
time.AfterFunc(time.Second*time.Duration(timeout), func() {
|
||||
atomic.AddInt64(&cacheLen, -1)
|
||||
globalMap.Delete(key)
|
||||
})
|
||||
}
|
||||
|
||||
func GetCache(key string) (interface{}, bool) {
|
||||
return globalMap.Load(key)
|
||||
}
|
||||
|
||||
func DeleteCache(key string) {
|
||||
globalMap.Delete(key)
|
||||
}
|
||||
|
||||
//func LenCache() int {
|
||||
// return int(cacheLen)
|
||||
//}
|
||||
75
pkg/util/encrypt/aes.go
Normal file
75
pkg/util/encrypt/aes.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
var AES_KEY = "ZRxS9kLzR90GLpCFabUWMKAw"
|
||||
|
||||
// AESEncrypt Aes加密 key: 24个字符
|
||||
func AESEncrypt(orig string, key string) (string, error) {
|
||||
// 转成字节数组
|
||||
origData := []byte(orig)
|
||||
k := []byte(key)
|
||||
// 分组秘钥
|
||||
// NewCipher该函数限制了输入k的长度必须为16, 24或者32
|
||||
block, err := aes.NewCipher(k)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 获取秘钥块的长度
|
||||
blockSize := block.BlockSize()
|
||||
// 补全码
|
||||
origData = PKCS7Padding(origData, blockSize)
|
||||
// 加密模式
|
||||
blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
|
||||
// 创建数组
|
||||
cryted := make([]byte, len(origData))
|
||||
// 加密
|
||||
blockMode.CryptBlocks(cryted, origData)
|
||||
return base64.StdEncoding.EncodeToString(cryted), nil
|
||||
}
|
||||
|
||||
// AESDecrypt Aes解密 key: 24个字符
|
||||
func AESDecrypt(crypt string, key string) (string, error) {
|
||||
// 转成字节数组
|
||||
cryptByte, err := base64.StdEncoding.DecodeString(crypt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
k := []byte(key)
|
||||
// 分组秘钥
|
||||
block, err := aes.NewCipher(k)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 获取秘钥块的长度
|
||||
blockSize := block.BlockSize()
|
||||
// 加密模式
|
||||
blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
|
||||
// 创建数组
|
||||
orig := make([]byte, len(cryptByte))
|
||||
// 解密
|
||||
blockMode.CryptBlocks(orig, cryptByte)
|
||||
// 去补全码
|
||||
orig = PKCS7UnPadding(orig)
|
||||
return string(orig), nil
|
||||
}
|
||||
|
||||
// PKCS7Padding 补码
|
||||
// AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
|
||||
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
paddingText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, paddingText...)
|
||||
}
|
||||
|
||||
// PKCS7UnPadding 去码
|
||||
func PKCS7UnPadding(origData []byte) []byte {
|
||||
length := len(origData)
|
||||
unpadding := int(origData[length-1])
|
||||
return origData[:(length - unpadding)]
|
||||
}
|
||||
15
pkg/util/encrypt/base64.go
Normal file
15
pkg/util/encrypt/base64.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package encrypt
|
||||
|
||||
import "encoding/base64"
|
||||
|
||||
// Base64Encrypt Base64加密
|
||||
func Base64Encrypt(str string) string {
|
||||
data := []byte(str)
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
// Base64Decrypt Base64解密
|
||||
func Base64Decrypt(str string) (string, error) {
|
||||
decodeBytes, err := base64.StdEncoding.DecodeString(str)
|
||||
return string(decodeBytes), err
|
||||
}
|
||||
93
pkg/util/encrypt/des.go
Normal file
93
pkg/util/encrypt/des.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
var DES_KEY = "fBEznwcv"
|
||||
|
||||
// DESEncrypt DES加密 key:8位
|
||||
func DESEncrypt(origData, key []byte) string {
|
||||
//将字节秘钥转换成block快
|
||||
block, _ := des.NewCipher(key)
|
||||
//对明文先进行补码操作
|
||||
origData = PKCS5Padding(origData, block.BlockSize())
|
||||
//设置加密方式
|
||||
blockMode := cipher.NewCBCEncrypter(block, key)
|
||||
//创建明文长度的字节数组
|
||||
crypted := make([]byte, len(origData))
|
||||
//加密明文,加密后的数据放到数组中
|
||||
blockMode.CryptBlocks(crypted, origData)
|
||||
return base64.StdEncoding.EncodeToString(crypted)
|
||||
}
|
||||
|
||||
// DESDecrypt DES解密 key:8位
|
||||
func DESDecrypt(data string, key []byte) string {
|
||||
//倒叙执行一遍加密方法
|
||||
//将字符串转换成字节数组
|
||||
crypted, _ := base64.StdEncoding.DecodeString(data)
|
||||
//将字节秘钥转换成block快
|
||||
block, _ := des.NewCipher(key)
|
||||
//设置解密方式
|
||||
blockMode := cipher.NewCBCDecrypter(block, key)
|
||||
//创建密文大小的数组变量
|
||||
origData := make([]byte, len(crypted))
|
||||
//解密密文到数组origData中
|
||||
blockMode.CryptBlocks(origData, crypted)
|
||||
//去补码
|
||||
origData = PKCS5UnPadding(origData)
|
||||
|
||||
return string(origData)
|
||||
}
|
||||
|
||||
// PKCS5Padding 实现明文的补码
|
||||
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
//计算出需要补多少位
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
//Repeat()函数的功能是把参数一 切片复制 参数二count个,然后合成一个新的字节切片返回
|
||||
// 需要补padding位的padding值
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
//把补充的内容拼接到明文后面
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
// PKCS5UnPadding 去除补码
|
||||
func PKCS5UnPadding(origData []byte) []byte {
|
||||
length := len(origData)
|
||||
// 去掉最后一个字节 unpadding 次
|
||||
unpadding := int(origData[length-1])
|
||||
//解密去补码时需取最后一个字节,值为m,则从数据尾部删除m个字节,剩余数据即为加密前的原文
|
||||
return origData[:(length - unpadding)]
|
||||
}
|
||||
|
||||
// TripleDESEncrypt 3DES加密 key:24位
|
||||
func TripleDESEncrypt(origData, key []byte) ([]byte, error) {
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
origData = PKCS5Padding(origData, block.BlockSize())
|
||||
// origData = ZeroPadding(origData, block.BlockSize())
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:8])
|
||||
crypted := make([]byte, len(origData))
|
||||
blockMode.CryptBlocks(crypted, origData)
|
||||
return crypted, nil
|
||||
}
|
||||
|
||||
// TripleDESDecrypt 3DES解密 key:24位
|
||||
func TripleDESDecrypt(crypted, key []byte) ([]byte, error) {
|
||||
block, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:8])
|
||||
origData := make([]byte, len(crypted))
|
||||
// origData := crypted
|
||||
blockMode.CryptBlocks(origData, crypted)
|
||||
origData = PKCS5UnPadding(origData)
|
||||
// origData = ZeroUnPadding(origData)
|
||||
return origData, nil
|
||||
}
|
||||
27
pkg/util/encrypt/md5.go
Normal file
27
pkg/util/encrypt/md5.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func Md5Fun01(str string) string {
|
||||
return fmt.Sprintf("%x", md5.Sum([]byte(str)))
|
||||
}
|
||||
|
||||
func Md5Fun02(str string) string {
|
||||
m := md5.New()
|
||||
m.Write([]byte(str))
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
||||
|
||||
func Md5Fun03(str string) string {
|
||||
m := md5.New()
|
||||
_, err := io.WriteString(m, str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fmt.Sprintf("%x", md5.Sum(nil))
|
||||
}
|
||||
64
pkg/util/encrypt/rsa.go
Normal file
64
pkg/util/encrypt/rsa.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// RSAEncrypt 加密
|
||||
func RSAEncrypt(origData []byte) ([]byte, error) {
|
||||
block, _ := pem.Decode(publicKey)
|
||||
if block == nil {
|
||||
return nil, errors.New("public key error")
|
||||
}
|
||||
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pub := pubInterface.(*rsa.PublicKey)
|
||||
return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
|
||||
}
|
||||
|
||||
// RSADecrypt 解密
|
||||
func RSADecrypt(ciphertext []byte) ([]byte, error) {
|
||||
block, _ := pem.Decode(privateKey)
|
||||
if block == nil {
|
||||
return nil, errors.New("private key error!")
|
||||
}
|
||||
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
|
||||
}
|
||||
|
||||
// 公钥和私钥可以从文件中读取
|
||||
var privateKey = []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y
|
||||
7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7
|
||||
Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB
|
||||
AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM
|
||||
ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1
|
||||
XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB
|
||||
/jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40
|
||||
IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG
|
||||
4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9
|
||||
DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8
|
||||
9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw
|
||||
DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO
|
||||
AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
var publicKey = []byte(`
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZsfv1qscqYdy4vY+P4e3cAtmv
|
||||
ppXQcRvrF1cB4drkv0haU24Y7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0Dgacd
|
||||
wYWd/7PeCELyEipZJL07Vro7Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NL
|
||||
AUeJ6PeW+DAkmJWF6QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
`)
|
||||
33
pkg/util/encrypt/sha.go
Normal file
33
pkg/util/encrypt/sha.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Sha1 加密
|
||||
func Sha1(str string) string {
|
||||
data := []byte(str)
|
||||
has := sha1.Sum(data)
|
||||
return fmt.Sprintf("%x", has)
|
||||
}
|
||||
|
||||
// Sha256 加密
|
||||
func Sha256(str string) string {
|
||||
w := sha256.New()
|
||||
io.WriteString(w, str)
|
||||
bw := w.Sum(nil)
|
||||
return hex.EncodeToString(bw)
|
||||
}
|
||||
|
||||
// Sha512 加密
|
||||
func Sha512(str string) string {
|
||||
w := sha512.New()
|
||||
io.WriteString(w, str)
|
||||
bw := w.Sum(nil)
|
||||
return hex.EncodeToString(bw)
|
||||
}
|
||||
195
pkg/util/fetcher.go
Normal file
195
pkg/util/fetcher.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/html/charset"
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var rateLimiter = time.Tick(20 * time.Millisecond)
|
||||
|
||||
func Fetch(url string) ([]byte, error) {
|
||||
<-rateLimiter
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
//fmt.Println("Error status code", resp.StatusCode)
|
||||
return nil, fmt.Errorf("error status code: %d", resp.StatusCode)
|
||||
}
|
||||
bodyReader := bufio.NewReader(resp.Body)
|
||||
e := determinEncoding(bodyReader)
|
||||
utf8Reader := transform.NewReader(bodyReader, e.NewDecoder())
|
||||
|
||||
return io.ReadAll(utf8Reader)
|
||||
}
|
||||
|
||||
func FetchHost(reqUrl, proxyIP string) ([]byte, error) {
|
||||
<-rateLimiter
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
//是否使用代理IP
|
||||
if proxyIP != "" {
|
||||
proxy, err := url.Parse(proxyIP)
|
||||
if err != nil {
|
||||
log.Fatalf("1:%v\n", err)
|
||||
}
|
||||
netTransport := &http.Transport{
|
||||
Proxy: http.ProxyURL(proxy),
|
||||
MaxIdleConnsPerHost: 10,
|
||||
ResponseHeaderTimeout: time.Second * time.Duration(5),
|
||||
}
|
||||
client = &http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
Transport: netTransport,
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", reqUrl, nil)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
req.Header.Set("Host", "www.stats.gov.cn")
|
||||
//req.Header.Set("Cookie", "_trs_uv=kma79tru_6_1yw6; SF_cookie_1=37059734; wzws_cid=ed0db2d09a630ccd20292459e4bfc8091d460d5990f415e3d928761ee970d90f7ba6254f9c4b8e9b79ad456094d4a1381305d0b065ff9cc539ee1775ec262f946af61ddbed371e2a6dae2bc2041a30fc")
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.54")
|
||||
req.Header.Set("Upgrade-Insecure-Requests", "1")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
//fmt.Println("Error status code", resp.StatusCode)
|
||||
return nil, fmt.Errorf("error status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
bodyReader := bufio.NewReader(resp.Body)
|
||||
e := determinEncoding(bodyReader)
|
||||
utf8Reader := transform.NewReader(bodyReader, e.NewDecoder())
|
||||
|
||||
return io.ReadAll(utf8Reader)
|
||||
}
|
||||
|
||||
// HttpPost 模拟请求方法
|
||||
func HttpPost(postUrl string, headers map[string]string, jsonMap map[string]interface{}, proxyIP string) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
//转换成postBody
|
||||
bytesData, err := json.Marshal(jsonMap)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
postBody := bytes.NewReader(bytesData)
|
||||
|
||||
//是否使用代理IP
|
||||
if proxyIP != "" {
|
||||
//proxy := func(_ *http.Request) (*url.URL, error) {
|
||||
// return url.Parse(proxyIP)
|
||||
//}
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
proxyUrl, err := url.Parse(proxyIP)
|
||||
if err == nil { // 使用传入代理
|
||||
transport.Proxy = http.ProxyURL(proxyUrl)
|
||||
}
|
||||
//&http.Transport{Proxy: proxy}
|
||||
client = &http.Client{Transport: transport}
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
// get请求
|
||||
req, err := http.NewRequest("GET", postUrl, postBody)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range headers {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
//fmt.Println("Error status code", resp.StatusCode)
|
||||
return nil, fmt.Errorf("error status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
bodyReader := bufio.NewReader(resp.Body)
|
||||
e := determinEncoding(bodyReader)
|
||||
utf8Reader := transform.NewReader(bodyReader, e.NewDecoder())
|
||||
|
||||
return io.ReadAll(utf8Reader)
|
||||
}
|
||||
|
||||
func determinEncoding(r *bufio.Reader) encoding.Encoding {
|
||||
bytes, err := r.Peek(1024)
|
||||
if err != nil && err != io.EOF {
|
||||
log.Printf("Fetcher error: %v", err)
|
||||
return unicode.UTF8
|
||||
}
|
||||
|
||||
e, _, _ := charset.DetermineEncoding(bytes, "")
|
||||
return e
|
||||
}
|
||||
|
||||
// PostRequest 请求
|
||||
func PostRequest(url, parameter string) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
byteParameter := bytes.NewBuffer([]byte(parameter))
|
||||
request, _ := http.NewRequest("POST", url, byteParameter)
|
||||
request.Header.Set("Content-type", "application/json")
|
||||
response, _ := client.Do(request)
|
||||
if response.StatusCode != 200 {
|
||||
return nil, errors.New("网络请求失败")
|
||||
}
|
||||
all, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("读取网络内容失败")
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// PostRequestString 请求
|
||||
func PostRequestString(url, parameter string) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
byteParameter := bytes.NewBuffer([]byte(parameter))
|
||||
request, _ := http.NewRequest("POST", url, byteParameter)
|
||||
request.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
||||
response, _ := client.Do(request)
|
||||
if response.StatusCode != 200 {
|
||||
return nil, errors.New("网络请求失败")
|
||||
}
|
||||
all, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("读取网络内容失败")
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
34
pkg/util/file.go
Normal file
34
pkg/util/file.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package util
|
||||
|
||||
import "os"
|
||||
|
||||
// 判断所给路径文件/文件夹是否存在
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path) //os.Stat获取文件信息
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果不存在则新建文件夹
|
||||
func IsNotExistMkDir(src string) error {
|
||||
if notExist := Exists(src); !notExist {
|
||||
if err := MkDir(src); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 新建文件夹
|
||||
func MkDir(src string) error {
|
||||
err := os.MkdirAll(src, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
140
pkg/util/location.go
Normal file
140
pkg/util/location.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LocationResponse struct {
|
||||
ShowApiResCode int `json:"showapi_res_code"`
|
||||
ShowApiResError string `json:"showapi_res_error"`
|
||||
ShowApiResBody LocationBodyResponse `json:"showapi_res_body"`
|
||||
}
|
||||
|
||||
type LocationBodyResponse struct {
|
||||
|
||||
// 0为成功,其他失败。失败时不扣点数
|
||||
RetCode int `json:"ret_code"`
|
||||
|
||||
// 1移动 2电信 3联通
|
||||
Type int `json:"type"`
|
||||
|
||||
// 号段 1890871
|
||||
Num int `json:"num"`
|
||||
|
||||
// 网络 服务商
|
||||
Name string `json:"name"`
|
||||
|
||||
// 此地区身份证号开头几位 530000
|
||||
ProvCode string `json:"prov_code"`
|
||||
|
||||
// 邮编 650000
|
||||
PostCode string `json:"post_code"`
|
||||
|
||||
// 省份 云南
|
||||
Prov string `json:"prov"`
|
||||
|
||||
// 城市 昆明
|
||||
City string `json:"city"`
|
||||
|
||||
// 区号 0871
|
||||
AreaCode string `json:"area_code"`
|
||||
}
|
||||
|
||||
func GetLocationWithPhone(phone string) (LocationResponse, error) {
|
||||
url := "http://showphone.market.alicloudapi.com/6-1?num=" + phone
|
||||
appCode := "ee6e2d724f3a42f4881c36ec90b9ef4c"
|
||||
client := &http.Client{Timeout: time.Second * 5}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
req.Header.Set("Authorization", "APPCODE "+appCode)
|
||||
|
||||
var out LocationResponse
|
||||
get, err := client.Do(req)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("接口请求失败:%v", err)
|
||||
}
|
||||
defer get.Body.Close()
|
||||
|
||||
res, err := ioutil.ReadAll(get.Body)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("body读取失败:%v", err)
|
||||
}
|
||||
|
||||
log.Printf("1: %#v", res)
|
||||
|
||||
err = json.Unmarshal(res, &out)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("json反序列化失败:%v", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type RegionResponse struct {
|
||||
ShowApiResCode int `json:"showapi_res_code"`
|
||||
ShowApiResError string `json:"showapi_res_error"`
|
||||
ShowApiResBody RegionBodyResponse `json:"showapi_res_body"`
|
||||
}
|
||||
|
||||
type RegionBodyResponse struct {
|
||||
|
||||
// 0为成功,其他失败。失败时不扣点数
|
||||
RetCode int `json:"ret_code"`
|
||||
|
||||
// 县
|
||||
county string `json:"county"`
|
||||
|
||||
// 网络 服务商
|
||||
ISP string `json:"isp"`
|
||||
|
||||
// IP
|
||||
IP string `json:"ip"`
|
||||
|
||||
// 国家
|
||||
Country string `json:"country"`
|
||||
|
||||
// 省份 云南
|
||||
Region string `json:"region"`
|
||||
|
||||
// 城市 昆明
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
func GetRegionWithIP(ip string) (RegionResponse, error) {
|
||||
url := "http://saip.market.alicloudapi.com/ip?ip=" + ip
|
||||
appCode := "ee6e2d724f3a42f4881c36ec90b9ef4c"
|
||||
client := &http.Client{Timeout: time.Second * 5}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
req.Header.Set("Authorization", "APPCODE "+appCode)
|
||||
|
||||
var out RegionResponse
|
||||
get, err := client.Do(req)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("接口请求失败:%v", err)
|
||||
}
|
||||
defer get.Body.Close()
|
||||
|
||||
res, err := ioutil.ReadAll(get.Body)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("body读取失败:%v", err)
|
||||
}
|
||||
|
||||
log.Printf("2: %#v", res)
|
||||
|
||||
err = json.Unmarshal(res, &out)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("json反序列化失败:%v", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
33
pkg/util/log.go
Normal file
33
pkg/util/log.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/natefinch/lumberjack"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func NewLogger() *zap.SugaredLogger {
|
||||
writeSyncer := getLogWriter()
|
||||
encoder := getEncoder()
|
||||
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
|
||||
logger := zap.New(core, zap.AddCaller())
|
||||
return logger.Sugar()
|
||||
}
|
||||
|
||||
func getEncoder() zapcore.Encoder {
|
||||
encoderConfig := zap.NewProductionEncoderConfig()
|
||||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
return zapcore.NewConsoleEncoder(encoderConfig)
|
||||
}
|
||||
|
||||
func getLogWriter() zapcore.WriteSyncer {
|
||||
lumberJackLogger := &lumberjack.Logger{
|
||||
Filename: "./log/run.log", // 日志文件的位置
|
||||
MaxSize: 10, // 在进行切割之前,日志文件的最大大小(以MB为单位)
|
||||
MaxBackups: 100, // 保留旧文件的最大个数
|
||||
MaxAge: 365, // 保留旧文件的最大天数
|
||||
Compress: false, // 是否压缩/归档旧文件
|
||||
}
|
||||
return zapcore.AddSync(lumberJackLogger)
|
||||
}
|
||||
17
pkg/util/md5.go
Normal file
17
pkg/util/md5.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Md5(str string) string {
|
||||
return fmt.Sprintf("%x", md5.Sum([]byte(str)))
|
||||
}
|
||||
|
||||
func EncodeMD5(value string) string {
|
||||
m := md5.New()
|
||||
m.Write([]byte(value))
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
||||
20
pkg/util/pager.go
Normal file
20
pkg/util/pager.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package util
|
||||
|
||||
// InitPageNo 初始化pageNo
|
||||
func InitPageNo(pageNo int) int {
|
||||
if pageNo < 1 {
|
||||
pageNo = 1
|
||||
}
|
||||
return pageNo
|
||||
}
|
||||
|
||||
// InitPageSize 初始化pageSize
|
||||
func InitPageSize(pageSize int) int {
|
||||
if pageSize < 1 {
|
||||
pageSize = 10
|
||||
}
|
||||
if pageSize > 20 {
|
||||
pageSize = 20
|
||||
}
|
||||
return pageSize
|
||||
}
|
||||
21
pkg/util/password.go
Normal file
21
pkg/util/password.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// HashPassword returns the bcrypt hash of the password
|
||||
func HashPassword(password string) (string, error) {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
return string(hashedPassword), nil
|
||||
}
|
||||
|
||||
// CheckPassword checks if the provided password is correct or not
|
||||
func CheckPassword(password string, hashedPassword string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||
}
|
||||
51
pkg/util/random.go
Normal file
51
pkg/util/random.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gonanoid "github.com/matoous/go-nanoid"
|
||||
)
|
||||
|
||||
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func RandomString(n int) string {
|
||||
var sb strings.Builder
|
||||
k := len(alphabet)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
c := alphabet[rand.Intn(k)]
|
||||
sb.WriteByte(c)
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func RandomInt(n int) string {
|
||||
var letters = []byte("0123456789")
|
||||
result := make([]byte, n)
|
||||
for i := range result {
|
||||
result[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func RandomLevel() string {
|
||||
levels := []string{"DEBUG", "INFO", "ERROR", "WARNING", "FAIL"}
|
||||
n := len(levels)
|
||||
return levels[rand.Intn(n)]
|
||||
}
|
||||
|
||||
func GenerateFilename() (string, error) {
|
||||
filename, err := gonanoid.Nanoid()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not generate avatar filename: %v", err)
|
||||
}
|
||||
return filename, nil
|
||||
}
|
||||
59
pkg/util/string.go
Normal file
59
pkg/util/string.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RemoveHTML(str string) string {
|
||||
if len(str) > 0 {
|
||||
//删除脚本
|
||||
reg := regexp.MustCompile(`([\r\n])[\s]+`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`<script[^>]*?>.*?</script>`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
//删除HTML
|
||||
reg = regexp.MustCompile(`<(.[^>]*)>`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`([\r\n])[\s]+`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`-->`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`<!--.*`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(quot|#34);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(amp|#38);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(lt|#60);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(gt|#62);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(nbsp|#160);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(iexcl|#161);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(cent|#162);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(pound|#163);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&(copy|#169);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
reg = regexp.MustCompile(`&#(\d+);`)
|
||||
str = reg.ReplaceAllString(str, "")
|
||||
|
||||
str = strings.ReplaceAll(str, "<", "")
|
||||
str = strings.ReplaceAll(str, ">", "")
|
||||
str = strings.ReplaceAll(str, "\n", "")
|
||||
str = strings.ReplaceAll(str, " ", "")
|
||||
str = strings.ReplaceAll(str, " ", "")
|
||||
|
||||
return str
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func FloatToString(val float64) string {
|
||||
return strconv.FormatFloat(val, 'f', 1, 64)
|
||||
}
|
||||
Reference in New Issue
Block a user