Initial commit: 网络验证平台

This commit is contained in:
Admin
2026-04-27 17:22:56 +08:00
commit afe67d704e
780 changed files with 88960 additions and 0 deletions
+229
View File
@@ -0,0 +1,229 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"io"
)
type EncryptType string
const (
EncryptTypeNone EncryptType = "none"
EncryptTypeAES EncryptType = "aes"
EncryptTypeRC4 EncryptType = "rc4"
)
type CryptoManager struct {
encryptType EncryptType
secretKey string
}
func NewCryptoManager(encryptType EncryptType, secretKey string) *CryptoManager {
return &CryptoManager{
encryptType: encryptType,
secretKey: secretKey,
}
}
func (cm *CryptoManager) GetEncryptType() EncryptType {
return cm.encryptType
}
func (cm *CryptoManager) GetSecretKey() string {
return cm.secretKey
}
func (cm *CryptoManager) Encrypt(plaintext string) (string, error) {
if cm.encryptType == EncryptTypeNone || cm.secretKey == "" {
return plaintext, nil
}
switch cm.encryptType {
case EncryptTypeAES:
return cm.encryptAES(plaintext)
case EncryptTypeRC4:
return cm.encryptRC4(plaintext)
default:
return plaintext, nil
}
}
func (cm *CryptoManager) Decrypt(ciphertext string) (string, error) {
if cm.encryptType == EncryptTypeNone || cm.secretKey == "" {
return ciphertext, nil
}
switch cm.encryptType {
case EncryptTypeAES:
return cm.decryptAES(ciphertext)
case EncryptTypeRC4:
return cm.decryptRC4(ciphertext)
default:
return ciphertext, nil
}
}
func (cm *CryptoManager) encryptAES(plaintext string) (string, error) {
key := []byte(cm.secretKey)
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
key = cm.padKey(key, 32)
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
func (cm *CryptoManager) decryptAES(ciphertext string) (string, error) {
key := []byte(cm.secretKey)
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
key = cm.padKey(key, 32)
}
data, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return "", err
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return "", errors.New("ciphertext too short")
}
nonce, ciphertextBytes := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertextBytes, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
func (cm *CryptoManager) encryptRC4(plaintext string) (string, error) {
key := []byte(cm.secretKey)
if len(key) == 0 {
return "", errors.New("secret key is empty")
}
cipher, err := NewRC4(key)
if err != nil {
return "", err
}
ciphertext := make([]byte, len(plaintext))
cipher.XORKeyStream(ciphertext, []byte(plaintext))
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
func (cm *CryptoManager) decryptRC4(ciphertext string) (string, error) {
key := []byte(cm.secretKey)
if len(key) == 0 {
return "", errors.New("secret key is empty")
}
data, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return "", err
}
cipher, err := NewRC4(key)
if err != nil {
return "", err
}
plaintext := make([]byte, len(data))
cipher.XORKeyStream(plaintext, data)
return string(plaintext), nil
}
func (cm *CryptoManager) padKey(key []byte, targetLen int) []byte {
if len(key) >= targetLen {
return key[:targetLen]
}
padded := make([]byte, targetLen)
copy(padded, key)
for i := len(key); i < targetLen; i++ {
padded[i] = key[i%len(key)]
}
return padded
}
type RC4 struct {
s [256]byte
i, j uint8
}
func NewRC4(key []byte) (*RC4, error) {
if len(key) == 0 {
return nil, errors.New("RC4 key cannot be empty")
}
rc4 := &RC4{}
for i := 0; i < 256; i++ {
rc4.s[i] = byte(i)
}
j := uint8(0)
for i := 0; i < 256; i++ {
j = j + rc4.s[i] + key[i%len(key)]
rc4.s[i], rc4.s[j] = rc4.s[j], rc4.s[i]
}
return rc4, nil
}
func (rc4 *RC4) XORKeyStream(dst, src []byte) {
for i := range src {
rc4.i++
rc4.j += rc4.s[rc4.i]
rc4.s[rc4.i], rc4.s[rc4.j] = rc4.s[rc4.j], rc4.s[rc4.i]
dst[i] = src[i] ^ rc4.s[(rc4.s[rc4.i]+rc4.s[rc4.j])]
}
}
func GenerateRandomKey(length int) (string, error) {
key := make([]byte, length)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(key), nil
}
func GenerateAESKey() (string, error) {
return GenerateRandomKey(32)
}
func GenerateRC4Key() (string, error) {
return GenerateRandomKey(16)
}
+75
View File
@@ -0,0 +1,75 @@
package jwt
import (
"errors"
"time"
"verification-platform-backend/internal/config"
"github.com/golang-jwt/jwt/v5"
)
// Claims JWT声明
type Claims struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
Role string `json:"role"`
jwt.RegisteredClaims
}
// GenerateToken 生成JWT令牌
func GenerateToken(userID uint, username, role string) (string, error) {
expireHours := config.GetInt("app.jwt_expire_hours")
if expireHours <= 0 {
expireHours = 24
}
expiresAt := time.Now().Add(time.Hour * time.Duration(expireHours))
claims := Claims{
UserID: userID,
Username: username,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expiresAt),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: config.GetString("app.name"),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(config.GetString("app.jwt_secret")))
}
// ParseToken 解析JWT令牌
func ParseToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(config.GetString("app.jwt_secret")), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
// RefreshToken 刷新令牌
func RefreshToken(tokenString string) (string, error) {
claims, err := ParseToken(tokenString)
if err != nil {
return "", err
}
// 检查令牌是否即将过期(30分钟内)
if time.Until(claims.ExpiresAt.Time) > 30*time.Minute {
return "", errors.New("token is still valid")
}
// 生成新令牌
return GenerateToken(claims.UserID, claims.Username, claims.Role)
}
+15
View File
@@ -0,0 +1,15 @@
package logger
import (
"log"
"os"
)
// Init 初始化日志
func Init() {
// 设置日志格式
log.SetFlags(log.LstdFlags | log.Lshortfile)
// 设置输出
log.SetOutput(os.Stdout)
}
+103
View File
@@ -0,0 +1,103 @@
package response
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Response 统一响应结构
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// PageResponse 分页响应结构
type PageResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
Total int64 `json:"total"`
Page int `json:"page"`
PerPage int `json:"per_page"`
}
// Success 成功响应
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 200,
Message: "操作成功",
Data: data,
})
}
func SuccessWithMessage(c *gin.Context, message string, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 200,
Message: message,
Data: data,
})
}
// Error 错误响应
func Error(c *gin.Context, httpCode int, message string) {
c.JSON(httpCode, Response{
Code: httpCode,
Message: message,
})
}
// ErrorWithData 带数据的错误响应
func ErrorWithData(c *gin.Context, httpCode int, message string, data interface{}) {
c.JSON(httpCode, Response{
Code: httpCode,
Message: message,
Data: data,
})
}
// ErrorWithCode 带自定义错误码的错误响应
func ErrorWithCode(c *gin.Context, httpCode int, code int, message string) {
c.JSON(httpCode, Response{
Code: code,
Message: message,
})
}
// PageSuccess 分页成功响应
func PageSuccess(c *gin.Context, data interface{}, total int64, page, perPage int) {
c.JSON(http.StatusOK, PageResponse{
Code: 0,
Message: "操作成功",
Data: data,
Total: total,
Page: page,
PerPage: perPage,
})
}
// ParamError 参数错误响应
func ParamError(c *gin.Context, message string) {
Error(c, http.StatusBadRequest, message)
}
// Unauthorized 未授权响应
func Unauthorized(c *gin.Context, message string) {
Error(c, http.StatusUnauthorized, message)
}
// Forbidden 禁止访问响应
func Forbidden(c *gin.Context, message string) {
Error(c, http.StatusForbidden, message)
}
// NotFound 资源未找到响应
func NotFound(c *gin.Context, message string) {
Error(c, http.StatusNotFound, message)
}
// InternalError 内部错误响应
func InternalError(c *gin.Context, message string) {
Error(c, http.StatusInternalServerError, message)
}
+42
View File
@@ -0,0 +1,42 @@
package utils
import (
"crypto/rand"
"encoding/base64"
"math/big"
)
// GenerateRandomString 生成随机字符串
func GenerateRandomString(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
b[i] = charset[n.Int64()]
}
return string(b)
}
// GenerateRandomNumber 生成随机数字
func GenerateRandomNumber(length int) string {
const charset = "0123456789"
b := make([]byte, length)
for i := range b {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
b[i] = charset[n.Int64()]
}
return string(b)
}
// GenerateCardKey 生成卡密
func GenerateCardKey(prefix string, length int) string {
randomPart := GenerateRandomString(length)
return prefix + "-" + randomPart
}
// GenerateBase64Token 生成Base64令牌
func GenerateBase64Token(length int) string {
b := make([]byte, length)
rand.Read(b)
return base64.URLEncoding.EncodeToString(b)
}