Initial commit: 网络验证平台
This commit is contained in:
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Init 初始化日志
|
||||
func Init() {
|
||||
// 设置日志格式
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
// 设置输出
|
||||
log.SetOutput(os.Stdout)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user