提交
This commit is contained in:
153
pkg/certificate/certificate.go
Normal file
153
pkg/certificate/certificate.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
certURL = "https://data-1251486259.cos.ap-beijing.myqcloud.com/copilot-ssl/ssl.pem"
|
||||
keyURL = "https://data-1251486259.cos.ap-beijing.myqcloud.com/copilot-ssl/ssl.key"
|
||||
certPath = "cert/ssl.pem"
|
||||
keyPath = "cert/ssl.key"
|
||||
checkInterval = 1 * time.Hour
|
||||
)
|
||||
|
||||
var (
|
||||
mutex sync.Mutex
|
||||
stopChan chan struct{}
|
||||
httpsServerReload chan struct{} // 用于通知需要重载服务器
|
||||
)
|
||||
|
||||
// InitCertificates 初始化证书管理
|
||||
func InitCertificates() (string, string, chan struct{}, error) {
|
||||
// 确保证书目录存在
|
||||
if err := os.MkdirAll(filepath.Dir(certPath), 0755); err != nil {
|
||||
return "", "", nil, fmt.Errorf("failed to create cert directory: %v", err)
|
||||
}
|
||||
|
||||
httpsServerReload = make(chan struct{}, 1)
|
||||
|
||||
// 首次检查和更新证书
|
||||
if err := checkAndUpdateCertificates(); err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
// 启动定时检查
|
||||
startPeriodicCheck()
|
||||
|
||||
return certPath, keyPath, httpsServerReload, nil
|
||||
}
|
||||
|
||||
// StopPeriodicCheck 停止定时检查
|
||||
func StopPeriodicCheck() {
|
||||
if stopChan != nil {
|
||||
close(stopChan)
|
||||
}
|
||||
}
|
||||
|
||||
func startPeriodicCheck() {
|
||||
stopChan = make(chan struct{})
|
||||
go func() {
|
||||
ticker := time.NewTicker(checkInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := checkAndUpdateCertificates(); err != nil {
|
||||
log.Printf("Error checking certificates: %v", err)
|
||||
}
|
||||
case <-stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func checkAndUpdateCertificates() error {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
needsUpdate, err := certificateNeedsUpdate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check certificate: %v", err)
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
if err := downloadFile(certURL, certPath); err != nil {
|
||||
return fmt.Errorf("failed to download certificate: %v", err)
|
||||
}
|
||||
if err := downloadFile(keyURL, keyPath); err != nil {
|
||||
return fmt.Errorf("failed to download key: %v", err)
|
||||
}
|
||||
|
||||
// 通知需要重载服务器
|
||||
select {
|
||||
case httpsServerReload <- struct{}{}:
|
||||
log.Println("Certificates updated, triggering server reload")
|
||||
default:
|
||||
// 如果通道已满,说明已经有一个重载信号在等待处理
|
||||
log.Println("Server reload already pending")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func certificateNeedsUpdate() (bool, error) {
|
||||
if !fileExists(certPath) || !fileExists(keyPath) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
certData, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to read certificate: %v", err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certData)
|
||||
if block == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
return time.Now().Add(24 * time.Hour).After(cert.NotAfter), nil
|
||||
}
|
||||
|
||||
func downloadFile(url string, filepath string) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("bad status: %s", resp.Status)
|
||||
}
|
||||
|
||||
out, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
96
pkg/crypto/AES.go
Normal file
96
pkg/crypto/AES.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
)
|
||||
|
||||
//高级加密标准(Adevanced Encryption Standard ,AES)
|
||||
|
||||
//key不能泄露
|
||||
|
||||
//PKCS7Padding PKCS7 填充模式
|
||||
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
//Repeat()函数的功能是把切片[]byte{byte(padding)}复制padding个,然后合并成新的字节切片返回
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
//PKCS7UnPadding 填充的反向操作,删除填充字符串
|
||||
func PKCS7UnPadding(origData []byte) ([]byte, error) {
|
||||
//获取数据长度
|
||||
length := len(origData)
|
||||
if length == 0 {
|
||||
return nil, errors.New("加密字符串错误!")
|
||||
} else {
|
||||
//获取填充字符串长度
|
||||
unpadding := int(origData[length-1])
|
||||
//截取切片,删除填充字节,并且返回明文
|
||||
return origData[:(length - unpadding)], nil
|
||||
}
|
||||
}
|
||||
|
||||
//AesEcrypt 实现加密
|
||||
func AesEcrypt(origData []byte, key []byte) ([]byte, error) {
|
||||
//创建加密算法实例
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//获取块的大小
|
||||
blockSize := block.BlockSize()
|
||||
//对数据进行填充,让数据长度满足需求
|
||||
origData = PKCS7Padding(origData, blockSize)
|
||||
//采用AES加密方法中CBC加密模式
|
||||
blocMode := cipher.NewCBCEncrypter(block, key[:blockSize])
|
||||
crypted := make([]byte, len(origData))
|
||||
//执行加密
|
||||
blocMode.CryptBlocks(crypted, origData)
|
||||
return crypted, nil
|
||||
}
|
||||
|
||||
//AesDeCrypt 实现解密
|
||||
func AesDeCrypt(cypted []byte, key []byte) ([]byte, error) {
|
||||
//创建加密算法实例
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//获取块大小
|
||||
blockSize := block.BlockSize()
|
||||
//创建加密客户端实例
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||
origData := make([]byte, len(cypted))
|
||||
//这个函数也可以用来解密
|
||||
blockMode.CryptBlocks(origData, cypted)
|
||||
//去除填充字符串
|
||||
origData, err = PKCS7UnPadding(origData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return origData, err
|
||||
}
|
||||
|
||||
//EnPwdCode 加密base64
|
||||
func EnPwdCode(pwd, PwdKey []byte) (string, error) {
|
||||
result, err := AesEcrypt(pwd, PwdKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(result), err
|
||||
}
|
||||
|
||||
//DePwdCode 解密
|
||||
func DePwdCode(pwd string, PwdKey []byte) ([]byte, error) {
|
||||
//解密base64字符串
|
||||
pwdByte, err := base64.StdEncoding.DecodeString(pwd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//执行AES解密
|
||||
return AesDeCrypt(pwdByte, PwdKey)
|
||||
}
|
||||
16
pkg/crypto/md5.go
Normal file
16
pkg/crypto/md5.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
//GetMd5 生成32位md5字串
|
||||
func GetMd5(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
104
pkg/crypto/sing.go
Normal file
104
pkg/crypto/sing.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetSign get the sign info
|
||||
func GetSign(data interface{}, appSecret string) string {
|
||||
md5ctx := md5.New()
|
||||
switch v := reflect.ValueOf(data); v.Kind() {
|
||||
case reflect.String:
|
||||
md5ctx.Write([]byte(v.String() + appSecret))
|
||||
return hex.EncodeToString(md5ctx.Sum(nil))
|
||||
case reflect.Struct:
|
||||
orderStr := StructToMapSing(v.Interface(), appSecret)
|
||||
md5ctx.Write([]byte(orderStr))
|
||||
return hex.EncodeToString(md5ctx.Sum(nil))
|
||||
case reflect.Ptr:
|
||||
originType := v.Elem().Type()
|
||||
if originType.Kind() != reflect.Struct {
|
||||
return ""
|
||||
}
|
||||
dataType := reflect.TypeOf(data).Elem()
|
||||
dataVal := v.Elem()
|
||||
orderStr := buildOrderStr(dataType, dataVal, appSecret)
|
||||
md5ctx.Write([]byte(orderStr))
|
||||
return hex.EncodeToString(md5ctx.Sum(nil))
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func buildOrderStr(t reflect.Type, v reflect.Value, appSecret string) (returnStr string) {
|
||||
keys := make([]string, 0, t.NumField())
|
||||
var data = make(map[string]interface{})
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if t.Field(i).Tag.Get("json") == "sign" {
|
||||
continue
|
||||
}
|
||||
data[t.Field(i).Tag.Get("json")] = v.Field(i).Interface()
|
||||
|
||||
keys = append(keys, t.Field(i).Tag.Get("json"))
|
||||
}
|
||||
sort.Sort(sort.StringSlice(keys))
|
||||
var buf bytes.Buffer
|
||||
for _, k := range keys {
|
||||
if data[k] == "" {
|
||||
continue
|
||||
}
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
|
||||
buf.WriteString(k)
|
||||
buf.WriteByte('=')
|
||||
switch vv := data[k].(type) {
|
||||
case string:
|
||||
buf.WriteString(vv)
|
||||
case int:
|
||||
case int8:
|
||||
case int16:
|
||||
case int32:
|
||||
case int64:
|
||||
buf.WriteString(strconv.FormatInt(int64(vv), 10))
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
buf.WriteString("&secret=" + appSecret)
|
||||
returnStr = buf.String()
|
||||
return returnStr
|
||||
}
|
||||
|
||||
func StructToMapSing(content interface{}, appSecret string) (returnStr string) {
|
||||
t := reflect.TypeOf(content)
|
||||
v := reflect.ValueOf(content)
|
||||
returnStr = buildOrderStr(t, v, appSecret)
|
||||
return returnStr
|
||||
}
|
||||
|
||||
func EnSign(query, body, key string) string {
|
||||
//加密算法
|
||||
//r:随机数
|
||||
//t:时间戳
|
||||
//q:query参数md5
|
||||
//b:Body参数md5
|
||||
//k:密钥
|
||||
//组合成 k=%s&r=%d&t=%d&q=%s&b=%s
|
||||
//进行md5
|
||||
//最后组合成 r,t,md5
|
||||
rand.Seed(time.Now().Unix())
|
||||
r := rand.Intn(800000) + 100000
|
||||
t := time.Now().Unix()
|
||||
str := fmt.Sprintf("k=%s&r=%d&t=%d&q=%s&b=%s", key, r, t, GetMd5(query), GetMd5(body))
|
||||
return fmt.Sprintf("%d,%d,%s", r, t, GetSign(str, key))
|
||||
}
|
||||
15
pkg/integral/file.go
Normal file
15
pkg/integral/file.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package integral
|
||||
|
||||
import "os"
|
||||
|
||||
//PathExists 路径是否存在
|
||||
func PathExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
179
pkg/jwt/core.go
Normal file
179
pkg/jwt/core.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoadModel 成员中套上jwt.RegisteredClaims
|
||||
type LoadModel interface {
|
||||
GetExpirationTime() (*jwt.NumericDate, error)
|
||||
GetIssuedAt() (*jwt.NumericDate, error)
|
||||
GetNotBefore() (*jwt.NumericDate, error)
|
||||
GetIssuer() (string, error)
|
||||
GetSubject() (string, error)
|
||||
GetAudience() (jwt.ClaimStrings, error)
|
||||
}
|
||||
|
||||
// JWT jwt对象
|
||||
type JWT struct {
|
||||
// 声明签名信息
|
||||
SigningKey []byte
|
||||
}
|
||||
|
||||
// NewJWT 初始化jwt对象
|
||||
func NewJWT() *JWT {
|
||||
return &JWT{
|
||||
[]byte(os.Getenv("TOKEN_SALT")),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateToken 调用jwt-go库生成token,编码的算法为jwt.SigningMethodHS256
|
||||
func (j *JWT) CreateToken(payload jwt.Claims) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
|
||||
return token.SignedString(j.SigningKey)
|
||||
}
|
||||
|
||||
// ParserToken 将token解码并验证
|
||||
func (j *JWT) ParserToken(tokenString string, model LoadModel) (jwt.Claims, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, model, func(token *jwt.Token) (interface{}, error) {
|
||||
return j.SigningKey, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = parserToken(token, model)
|
||||
if token.Valid {
|
||||
return token.Claims, nil
|
||||
}
|
||||
return nil, fmt.Errorf("token无效")
|
||||
}
|
||||
|
||||
func parserToken[T LoadModel](token *jwt.Token, model T) (jwt.Claims, error) {
|
||||
if claims, ok := token.Claims.(T); !ok {
|
||||
return nil, fmt.Errorf("token结构错误")
|
||||
} else {
|
||||
return claims, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CreateToken 用于快速生成一个Token
|
||||
// UserLoad 用户负载
|
||||
// ExpiresAt 多少秒之后过期,单位:秒
|
||||
// Issuer 签名颁发着
|
||||
func CreateToken(JWTLoad jwt.Claims) (Token string, err error) {
|
||||
// 构造SignKey: 签名和解签名需要使用一个值
|
||||
j := NewJWT()
|
||||
// 构造用户claims信息(负荷)
|
||||
token, err := j.CreateToken(JWTLoad)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// CreateStandardClaims 快速创建签名数据
|
||||
func CreateStandardClaims(ExpiresAt int64, Issuer string) jwt.RegisteredClaims {
|
||||
return jwt.RegisteredClaims{
|
||||
Issuer: Issuer, // 签名颁发者
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(ExpiresAt))), // 签名过期时间
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()), //
|
||||
NotBefore: jwt.NewNumericDate(time.Now()), // 签名生效时间
|
||||
}
|
||||
}
|
||||
|
||||
// CheckToken 用于检查Token是否有效
|
||||
// issuer为可选参数
|
||||
func CheckToken[T LoadModel](token string, model T, issuer string) (bool, T, error) {
|
||||
j := NewJWT()
|
||||
load, err := j.ParserToken(token, model)
|
||||
if err != nil {
|
||||
return false, model, err
|
||||
}
|
||||
expTime, err := load.GetExpirationTime()
|
||||
if expTime.Unix() < time.Now().Unix() {
|
||||
return false, model, errors.New("token已过期,请重新登录")
|
||||
}
|
||||
loadIssuer, err := load.GetIssuer()
|
||||
if err != nil {
|
||||
return false, model, err
|
||||
}
|
||||
if issuer == loadIssuer {
|
||||
return true, load.(T), nil
|
||||
}
|
||||
return false, model, errors.New("token签名错误,请重新登录")
|
||||
}
|
||||
|
||||
// GetJwtProto 从Gin中获取Jwt原型体
|
||||
// model为业务内jwt模型
|
||||
func GetJwtProto[T any](c *gin.Context, model T) (T, error) {
|
||||
tokener, _ := c.Get("token")
|
||||
if tokener == nil {
|
||||
return model, errors.New("JWT Is NULL")
|
||||
}
|
||||
token, ok := tokener.(T)
|
||||
if !ok {
|
||||
return model, errors.New("JWTLoad Error")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetTokenLoad 用于从GinContext中取回JWTUsermapm
|
||||
func GetTokenLoad(c *gin.Context) (*JWTLoad, map[string]interface{}) {
|
||||
token, _ := c.Get("token")
|
||||
if token == nil {
|
||||
return nil, nil
|
||||
}
|
||||
load := token.(*JWTLoad)
|
||||
if load.UserLoad == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return load, load.UserLoad.(map[string]interface{})
|
||||
}
|
||||
|
||||
func ShouldBindTokenLoad(c *gin.Context, obj any) error {
|
||||
var e error
|
||||
token, _ := c.Get("token")
|
||||
if token == nil {
|
||||
e = errors.New("token is invalid")
|
||||
}
|
||||
load := token.(*JWTLoad)
|
||||
if load.UserLoad == nil {
|
||||
e = errors.New("token is illegal")
|
||||
}
|
||||
for k, v := range load.UserLoad.(map[string]interface{}) {
|
||||
fmt.Println(v, k)
|
||||
e = SetField(obj, k, v)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func SetField(obj interface{}, name string, value interface{}) error {
|
||||
structValue := reflect.ValueOf(obj).Elem()
|
||||
structFieldValue := structValue.FieldByName(name)
|
||||
fmt.Println(structFieldValue)
|
||||
|
||||
if !structFieldValue.IsValid() {
|
||||
return fmt.Errorf("no such field: %s in obj", name)
|
||||
}
|
||||
|
||||
if !structFieldValue.CanSet() {
|
||||
return fmt.Errorf("cannot set %s field value", name)
|
||||
}
|
||||
|
||||
structFieldType := structFieldValue.Type()
|
||||
val := reflect.ValueOf(value)
|
||||
if structFieldType != val.Type() {
|
||||
return errors.New("provided value type didn't match obj field type")
|
||||
}
|
||||
|
||||
structFieldValue.Set(val)
|
||||
return nil
|
||||
}
|
||||
46
pkg/jwt/examples_test.go
Normal file
46
pkg/jwt/examples_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package jwt_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"ripper/internal/middleware"
|
||||
"ripper/internal/response"
|
||||
jwtpkg "ripper/pkg/jwt"
|
||||
)
|
||||
|
||||
// 业务模型
|
||||
type UserLoad struct {
|
||||
ID uint `json:"id"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// 快速创建接口
|
||||
func newUserLoad(ID uint, ExpiresAt int64, Issuer string) *UserLoad {
|
||||
return &UserLoad{
|
||||
ID: ID,
|
||||
RegisteredClaims: jwtpkg.CreateStandardClaims(ExpiresAt, Issuer),
|
||||
}
|
||||
}
|
||||
|
||||
func testHandler(c *gin.Context) {
|
||||
// 获取jwt数据并处理错误
|
||||
// 可以在中间件完成这一步,根据业务自行扩展
|
||||
token, err := jwtpkg.GetJwtProto(c, &UserLoad{})
|
||||
if err != nil {
|
||||
response.FailJson(c, response.FailStruct{
|
||||
Code: 401,
|
||||
Msg: err.Error(),
|
||||
}, false)
|
||||
}
|
||||
fmt.Println(token)
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
//使用中间件时绑定业务jwt模型,并且可以设置验证的签发人
|
||||
r.Use(middleware.JWTAuth(&UserLoad{}, "is"))
|
||||
r.GET("/test", testHandler)
|
||||
r.Run()
|
||||
}
|
||||
9
pkg/jwt/model.go
Normal file
9
pkg/jwt/model.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package jwt
|
||||
|
||||
import "github.com/golang-jwt/jwt/v5"
|
||||
|
||||
type JWTLoad struct {
|
||||
UserLoad interface{} `json:"user_load"`
|
||||
Version int64
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
5
pkg/logger/error.go
Normal file
5
pkg/logger/error.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package logger
|
||||
|
||||
func Error(err error) {
|
||||
|
||||
}
|
||||
11
pkg/message/message_other.go
Normal file
11
pkg/message/message_other.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build !windows
|
||||
|
||||
package message
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
func ShowAppLaunchMessage() {
|
||||
log.Printf("%s: %s\n", "运行成功", "服务已经启动.")
|
||||
}
|
||||
24
pkg/message/message_windows.go
Normal file
24
pkg/message/message_windows.go
Normal file
@@ -0,0 +1,24 @@
|
||||
//go:build windows
|
||||
|
||||
package message
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
MB_OK = 0x00000000
|
||||
MB_ICONINFORMATION = 0x00000040
|
||||
)
|
||||
|
||||
var (
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
messageBoxW = user32.NewProc("MessageBoxW")
|
||||
)
|
||||
|
||||
func ShowAppLaunchMessage() {
|
||||
titlePtr, _ := syscall.UTF16PtrFromString("运行成功")
|
||||
textPtr, _ := syscall.UTF16PtrFromString("服务已经启动, GUI的程序将会以后台方式运行, 如需关闭请手动结束进程.")
|
||||
messageBoxW.Call(0, uintptr(unsafe.Pointer(textPtr)), uintptr(unsafe.Pointer(titlePtr)), MB_OK|MB_ICONINFORMATION)
|
||||
}
|
||||
19
pkg/util/crypto.go
Normal file
19
pkg/util/crypto.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"ripper/pkg/crypto"
|
||||
)
|
||||
|
||||
// CheckPassword 用于将用户输入的密码与数据库取出的密码进行比对
|
||||
func CheckPassword(PlainText, SecretKey, CipherText string) bool {
|
||||
chK, _ := crypto.AesEcrypt([]byte(PlainText), []byte(SecretKey))
|
||||
return fmt.Sprintf("%x", md5.Sum(chK)) == CipherText
|
||||
}
|
||||
|
||||
// CreatePassword 用于将用户输入的密码进行加密
|
||||
func CreatePassword(SecretKey, PlainText string) string {
|
||||
chK, _ := crypto.AesEcrypt([]byte(PlainText), []byte(SecretKey))
|
||||
return fmt.Sprintf("%x", md5.Sum(chK))
|
||||
}
|
||||
63
pkg/util/encrypt.go
Normal file
63
pkg/util/encrypt.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EmojiEncode Emoji表情转码
|
||||
func EmojiDecode(s string) string {
|
||||
//emoji表情的数据表达式
|
||||
var re *regexp.Regexp
|
||||
var reg *regexp.Regexp
|
||||
var src []string
|
||||
re = regexp.MustCompile("\\[[\\\\u0-9a-zA-Z]")
|
||||
//提取emoji数据表达式
|
||||
reg = regexp.MustCompile("\\[\\\\u|]")
|
||||
src = re.FindAllString(s, -1)
|
||||
for i := 0; i < len(src); i++ {
|
||||
var e = reg.ReplaceAllString(src[i], "")
|
||||
var p, err = strconv.ParseInt(e, 16, 32)
|
||||
if err == nil {
|
||||
s = strings.Replace(s, src[i], string(rune(p)), -1)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 表情转换
|
||||
func EmojiCode(s string) string {
|
||||
var ret string
|
||||
var rs []rune
|
||||
rs = []rune(s)
|
||||
|
||||
for i := 0; i < len(rs); i++ {
|
||||
if len(string(rs[i])) == 4 {
|
||||
ret += `[\u` + strconv.FormatInt(int64(rs[i]), 16) + `]`
|
||||
} else {
|
||||
ret += string(rs[i])
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// BaseEncode Base64编码
|
||||
func BaseEncode(s string) string {
|
||||
data := []byte(s)
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
|
||||
base64.StdEncoding.Encode(dst, data)
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
// BaseDecode Base64解码
|
||||
func BaseDecode(s string) string {
|
||||
data := []byte(s)
|
||||
dst := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
|
||||
n, err := base64.StdEncoding.Decode(dst, data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(dst[:n])
|
||||
}
|
||||
9
pkg/util/memory.go
Normal file
9
pkg/util/memory.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package util
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func DeepCoyp(s string) string {
|
||||
b := make([]byte, len(s))
|
||||
copy(b, s)
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
50
pkg/util/misc.go
Normal file
50
pkg/util/misc.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/gofrs/uuid"
|
||||
"math/rand"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Ifs 三目运算的函数
|
||||
func Ifs[T any](a bool, b, c T) T {
|
||||
if a {
|
||||
return b
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func GetUUID() (string, error) {
|
||||
u2, err := uuid.NewV4()
|
||||
return u2.String(), err
|
||||
}
|
||||
|
||||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
var src = rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
const (
|
||||
// 6 bits to represent a letter index
|
||||
letterIdBits = 6
|
||||
// All 1-bits as many as letterIdBits
|
||||
letterIdMask = 1<<letterIdBits - 1
|
||||
letterIdMax = 63 / letterIdBits
|
||||
)
|
||||
|
||||
func RandomStr(n int) string {
|
||||
b := make([]byte, n)
|
||||
// A rand.Int63() generates 63 random bits, enough for letterIdMax letters!
|
||||
for i, cache, remain := n-1, src.Int63(), letterIdMax; i >= 0; {
|
||||
if remain == 0 {
|
||||
cache, remain = src.Int63(), letterIdMax
|
||||
}
|
||||
if idx := int(cache & letterIdMask); idx < len(letters) {
|
||||
b[i] = letters[idx]
|
||||
i--
|
||||
}
|
||||
cache >>= letterIdBits
|
||||
remain--
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
Reference in New Issue
Block a user