添加更多的方法
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
package utilsx
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenerateRandomString 生成指定长度的随机字符串
|
||||
func GenerateRandomString(length int) (string, error) {
|
||||
if length <= 0 {
|
||||
return "", fmt.Errorf("长度必须大于0")
|
||||
}
|
||||
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
result := make([]byte, length)
|
||||
|
||||
for i := range result {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("生成随机数失败: %v", err)
|
||||
}
|
||||
result[i] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
// GenerateRandomBytes 生成指定长度的随机字节
|
||||
func GenerateRandomBytes(length int) ([]byte, error) {
|
||||
if length <= 0 {
|
||||
return nil, fmt.Errorf("长度必须大于0")
|
||||
}
|
||||
|
||||
bytes := make([]byte, length)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("生成随机字节失败: %v", err)
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// GenerateRandomHexString 生成指定长度的随机十六进制字符串
|
||||
func GenerateRandomHexString(length int) (string, error) {
|
||||
bytes, err := GenerateRandomBytes(length)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
// GenerateRandomBase64String 生成指定长度的随机Base64字符串
|
||||
func GenerateRandomBase64String(length int) (string, error) {
|
||||
bytes, err := GenerateRandomBytes(length)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
// CalculateHash 计算字符串的哈希值
|
||||
func CalculateHash(input string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(input))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// GenerateUUID 生成UUID v4
|
||||
func GenerateUUID() (string, error) {
|
||||
bytes := make([]byte, 16)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("生成UUID失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置版本号和变体
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40 // Version 4
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80 // Variant 10
|
||||
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", bytes[0:4], bytes[4:6], bytes[6:8], bytes[8:10], bytes[10:16]), nil
|
||||
}
|
||||
|
||||
// GenerateAPIKey 生成API密钥
|
||||
func GenerateAPIKey(prefix string) (string, error) {
|
||||
// 生成24字节的随机数据
|
||||
randomBytes, err := GenerateRandomBytes(24)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("生成API密钥失败: %v", err)
|
||||
}
|
||||
|
||||
// 转换为Base64
|
||||
apiKey := base64.StdEncoding.EncodeToString(randomBytes)
|
||||
|
||||
// 添加前缀(如果有)
|
||||
if prefix != "" {
|
||||
apiKey = prefix + "." + apiKey
|
||||
}
|
||||
|
||||
return apiKey, nil
|
||||
}
|
||||
|
||||
// GenerateToken 生成安全令牌
|
||||
func GenerateToken(length int) (string, error) {
|
||||
if length <= 0 {
|
||||
return "", fmt.Errorf("长度必须大于0")
|
||||
}
|
||||
|
||||
// 生成足够的随机字节,确保Base64编码后至少有指定长度
|
||||
bytesNeeded := (length*3 + 3) / 4 // Base64编码会使长度增加约33%
|
||||
bytes, err := GenerateRandomBytes(bytesNeeded)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("生成令牌失败: %v", err)
|
||||
}
|
||||
|
||||
// 转换为Base64 URL安全编码(无填充,URL和文件名安全)
|
||||
token := base64.URLEncoding.EncodeToString(bytes)
|
||||
|
||||
// 移除填充字符
|
||||
token = strings.TrimRight(token, "=")
|
||||
|
||||
// 确保令牌长度符合要求
|
||||
if len(token) > length {
|
||||
token = token[:length]
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GeneratePassword 生成密码
|
||||
func GeneratePassword(length int, useUppercase, useLowercase, useDigits, useSymbols bool) (string, error) {
|
||||
if length <= 0 {
|
||||
return "", fmt.Errorf("密码长度必须大于0")
|
||||
}
|
||||
|
||||
var charset string
|
||||
if useUppercase {
|
||||
charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
}
|
||||
if useLowercase {
|
||||
charset += "abcdefghijklmnopqrstuvwxyz"
|
||||
}
|
||||
if useDigits {
|
||||
charset += "0123456789"
|
||||
}
|
||||
if useSymbols {
|
||||
charset += "!@#$%^&*()-_=+[]{}|;:,.<>?"
|
||||
}
|
||||
|
||||
if charset == "" {
|
||||
return "", fmt.Errorf("至少选择一种字符类型")
|
||||
}
|
||||
|
||||
// 确保密码包含所有选择的字符类型
|
||||
password := make([]byte, length)
|
||||
charsetLength := big.NewInt(int64(len(charset)))
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
num, err := rand.Int(rand.Reader, charsetLength)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("生成密码失败: %v", err)
|
||||
}
|
||||
password[i] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
return string(password), nil
|
||||
}
|
||||
|
||||
// HashPassword 哈希密码(简单示例,实际应用中应使用更安全的算法如bcrypt)
|
||||
func HashPassword(password, salt string) string {
|
||||
h := sha1.New()
|
||||
io.WriteString(h, password+salt)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// VerifyPassword 验证密码(简单示例,实际应用中应使用更安全的算法如bcrypt)
|
||||
func VerifyPassword(password, salt, hash string) bool {
|
||||
return HashPassword(password, salt) == hash
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
package utilsx
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateRandomString(t *testing.T) {
|
||||
// 测试生成随机字符串
|
||||
str, err := GenerateRandomString(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机字符串失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(str) != 10 {
|
||||
t.Errorf("生成的字符串长度不正确,期望: 10, 实际: %d", len(str))
|
||||
}
|
||||
|
||||
// 测试长度为0
|
||||
_, err = GenerateRandomString(0)
|
||||
if err == nil {
|
||||
t.Errorf("长度为0应该返回错误")
|
||||
}
|
||||
|
||||
// 测试多次生成,应该不同
|
||||
str2, err := GenerateRandomString(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机字符串失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if str == str2 {
|
||||
t.Errorf("多次生成的随机字符串应该不同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRandomBytes(t *testing.T) {
|
||||
// 测试生成随机字节
|
||||
bytes, err := GenerateRandomBytes(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机字节失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(bytes) != 10 {
|
||||
t.Errorf("生成的字节长度不正确,期望: 10, 实际: %d", len(bytes))
|
||||
}
|
||||
|
||||
// 测试长度为0
|
||||
_, err = GenerateRandomBytes(0)
|
||||
if err == nil {
|
||||
t.Errorf("长度为0应该返回错误")
|
||||
}
|
||||
|
||||
// 测试多次生成,应该不同
|
||||
bytes2, err := GenerateRandomBytes(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机字节失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 比较字节切片
|
||||
equal := true
|
||||
for i := range bytes {
|
||||
if bytes[i] != bytes2[i] {
|
||||
equal = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if equal {
|
||||
t.Errorf("多次生成的随机字节应该不同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRandomHexString(t *testing.T) {
|
||||
// 测试生成随机十六进制字符串
|
||||
hexStr, err := GenerateRandomHexString(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机十六进制字符串失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 十六进制字符串长度应该是字节长度的两倍
|
||||
if len(hexStr) != 20 {
|
||||
t.Errorf("生成的十六进制字符串长度不正确,期望: 20, 实际: %d", len(hexStr))
|
||||
}
|
||||
|
||||
// 验证是否为有效的十六进制字符串
|
||||
_, err = hex.DecodeString(hexStr)
|
||||
if err != nil {
|
||||
t.Errorf("生成的字符串不是有效的十六进制: %v", err)
|
||||
}
|
||||
|
||||
// 测试多次生成,应该不同
|
||||
hexStr2, err := GenerateRandomHexString(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机十六进制字符串失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if hexStr == hexStr2 {
|
||||
t.Errorf("多次生成的随机十六进制字符串应该不同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRandomBase64String(t *testing.T) {
|
||||
// 测试生成随机Base64字符串
|
||||
base64Str, err := GenerateRandomBase64String(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机Base64字符串失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证是否为有效的Base64字符串
|
||||
_, err = base64.StdEncoding.DecodeString(base64Str)
|
||||
if err != nil {
|
||||
t.Errorf("生成的字符串不是有效的Base64: %v", err)
|
||||
}
|
||||
|
||||
// 测试多次生成,应该不同
|
||||
base64Str2, err := GenerateRandomBase64String(10)
|
||||
if err != nil {
|
||||
t.Errorf("生成随机Base64字符串失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if base64Str == base64Str2 {
|
||||
t.Errorf("多次生成的随机Base64字符串应该不同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateHash(t *testing.T) {
|
||||
// 测试计算哈希
|
||||
input := "test input"
|
||||
hash := CalculateHash(input)
|
||||
|
||||
// SHA1哈希应该是40个字符
|
||||
if len(hash) != 40 {
|
||||
t.Errorf("哈希长度不正确,期望: 40, 实际: %d", len(hash))
|
||||
}
|
||||
|
||||
// 相同输入应该产生相同哈希
|
||||
hash2 := CalculateHash(input)
|
||||
if hash != hash2 {
|
||||
t.Errorf("相同输入产生了不同的哈希")
|
||||
}
|
||||
|
||||
// 不同输入应该产生不同哈希
|
||||
hash3 := CalculateHash("different input")
|
||||
if hash == hash3 {
|
||||
t.Errorf("不同输入产生了相同的哈希")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateUUID(t *testing.T) {
|
||||
// 测试生成UUID
|
||||
uuid, err := GenerateUUID()
|
||||
if err != nil {
|
||||
t.Errorf("生成UUID失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// UUID应该是36个字符(包括4个连字符)
|
||||
if len(uuid) != 36 {
|
||||
t.Errorf("UUID长度不正确,期望: 36, 实际: %d", len(uuid))
|
||||
}
|
||||
|
||||
// UUID应该包含4个连字符
|
||||
if strings.Count(uuid, "-") != 4 {
|
||||
t.Errorf("UUID应该包含4个连字符")
|
||||
}
|
||||
|
||||
// 测试多次生成,应该不同
|
||||
uuid2, err := GenerateUUID()
|
||||
if err != nil {
|
||||
t.Errorf("生成UUID失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if uuid == uuid2 {
|
||||
t.Errorf("多次生成的UUID应该不同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateAPIKey(t *testing.T) {
|
||||
// 测试生成API密钥(无前缀)
|
||||
apiKey, err := GenerateAPIKey("")
|
||||
if err != nil {
|
||||
t.Errorf("生成API密钥失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证是否为有效的Base64
|
||||
_, err = base64.StdEncoding.DecodeString(apiKey)
|
||||
if err != nil {
|
||||
t.Errorf("生成的API密钥不是有效的Base64: %v", err)
|
||||
}
|
||||
|
||||
// 测试生成API密钥(有前缀)
|
||||
apiKeyWithPrefix, err := GenerateAPIKey("test")
|
||||
if err != nil {
|
||||
t.Errorf("生成API密钥失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 应该包含前缀
|
||||
if !strings.HasPrefix(apiKeyWithPrefix, "test.") {
|
||||
t.Errorf("API密钥应该包含前缀")
|
||||
}
|
||||
|
||||
// 去掉前缀后应该是有效的Base64
|
||||
parts := strings.Split(apiKeyWithPrefix, ".")
|
||||
if len(parts) != 2 {
|
||||
t.Errorf("API密钥格式不正确")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = base64.StdEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
t.Errorf("生成的API密钥不是有效的Base64: %v", err)
|
||||
}
|
||||
|
||||
// 测试多次生成,应该不同
|
||||
apiKey2, err := GenerateAPIKey("")
|
||||
if err != nil {
|
||||
t.Errorf("生成API密钥失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if apiKey == apiKey2 {
|
||||
t.Errorf("多次生成的API密钥应该不同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateToken(t *testing.T) {
|
||||
// 测试生成令牌
|
||||
token, err := GenerateToken(20)
|
||||
if err != nil {
|
||||
t.Errorf("生成令牌失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(token) != 20 {
|
||||
t.Errorf("令牌长度不正确,期望: 20, 实际: %d", len(token))
|
||||
}
|
||||
|
||||
// 令牌应该不包含填充字符
|
||||
if strings.Contains(token, "=") {
|
||||
t.Errorf("令牌不应该包含填充字符")
|
||||
}
|
||||
|
||||
// 测试长度为0
|
||||
_, err = GenerateToken(0)
|
||||
if err == nil {
|
||||
t.Errorf("长度为0应该返回错误")
|
||||
}
|
||||
|
||||
// 测试多次生成,应该不同
|
||||
token2, err := GenerateToken(20)
|
||||
if err != nil {
|
||||
t.Errorf("生成令牌失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if token == token2 {
|
||||
t.Errorf("多次生成的令牌应该不同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratePassword(t *testing.T) {
|
||||
// 测试生成密码(所有字符类型)
|
||||
password, err := GeneratePassword(12, true, true, true, true)
|
||||
if err != nil {
|
||||
t.Errorf("生成密码失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(password) != 12 {
|
||||
t.Errorf("密码长度不正确,期望: 12, 实际: %d", len(password))
|
||||
}
|
||||
|
||||
// 测试只使用大写字母
|
||||
passwordUpper, err := GeneratePassword(10, true, false, false, false)
|
||||
if err != nil {
|
||||
t.Errorf("生成密码失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证只包含大写字母
|
||||
for _, c := range passwordUpper {
|
||||
if !(c >= 'A' && c <= 'Z') {
|
||||
t.Errorf("密码包含非大写字母字符: %c", c)
|
||||
}
|
||||
}
|
||||
|
||||
// 测试只使用小写字母
|
||||
passwordLower, err := GeneratePassword(10, false, true, false, false)
|
||||
if err != nil {
|
||||
t.Errorf("生成密码失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证只包含小写字母
|
||||
for _, c := range passwordLower {
|
||||
if !(c >= 'a' && c <= 'z') {
|
||||
t.Errorf("密码包含非小写字母字符: %c", c)
|
||||
}
|
||||
}
|
||||
|
||||
// 测试只使用数字
|
||||
passwordDigits, err := GeneratePassword(10, false, false, true, false)
|
||||
if err != nil {
|
||||
t.Errorf("生成密码失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证只包含数字
|
||||
for _, c := range passwordDigits {
|
||||
if !(c >= '0' && c <= '9') {
|
||||
t.Errorf("密码包含非数字字符: %c", c)
|
||||
}
|
||||
}
|
||||
|
||||
// 测试长度为0
|
||||
_, err = GeneratePassword(0, true, true, true, true)
|
||||
if err == nil {
|
||||
t.Errorf("长度为0应该返回错误")
|
||||
}
|
||||
|
||||
// 测试不选择任何字符类型
|
||||
_, err = GeneratePassword(10, false, false, false, false)
|
||||
if err == nil {
|
||||
t.Errorf("不选择任何字符类型应该返回错误")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashPassword(t *testing.T) {
|
||||
// 测试哈希密码
|
||||
password := "test_password"
|
||||
salt := "test_salt"
|
||||
|
||||
hash := HashPassword(password, salt)
|
||||
|
||||
// SHA1哈希应该是40个字符
|
||||
if len(hash) != 40 {
|
||||
t.Errorf("哈希长度不正确,期望: 40, 实际: %d", len(hash))
|
||||
}
|
||||
|
||||
// 相同密码和盐应该产生相同哈希
|
||||
hash2 := HashPassword(password, salt)
|
||||
if hash != hash2 {
|
||||
t.Errorf("相同密码和盐产生了不同的哈希")
|
||||
}
|
||||
|
||||
// 不同盐应该产生不同哈希
|
||||
hash3 := HashPassword(password, "different_salt")
|
||||
if hash == hash3 {
|
||||
t.Errorf("不同盐产生了相同的哈希")
|
||||
}
|
||||
|
||||
// 不同密码应该产生不同哈希
|
||||
hash4 := HashPassword("different_password", salt)
|
||||
if hash == hash4 {
|
||||
t.Errorf("不同密码产生了相同的哈希")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyPassword(t *testing.T) {
|
||||
// 测试验证密码
|
||||
password := "test_password"
|
||||
salt := "test_salt"
|
||||
|
||||
// 正确的密码应该验证通过
|
||||
hash := HashPassword(password, salt)
|
||||
if !VerifyPassword(password, salt, hash) {
|
||||
t.Errorf("正确的密码验证失败")
|
||||
}
|
||||
|
||||
// 错误的密码应该验证失败
|
||||
if VerifyPassword("wrong_password", salt, hash) {
|
||||
t.Errorf("错误的密码验证通过")
|
||||
}
|
||||
|
||||
// 错误的盐应该验证失败
|
||||
if VerifyPassword(password, "wrong_salt", hash) {
|
||||
t.Errorf("错误的盐验证通过")
|
||||
}
|
||||
}
|
||||
|
||||
// 基准测试
|
||||
func BenchmarkGenerateRandomString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GenerateRandomString(16)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGenerateRandomBytes(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GenerateRandomBytes(16)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCalculateHash(b *testing.B) {
|
||||
input := "benchmark test string"
|
||||
for i := 0; i < b.N; i++ {
|
||||
CalculateHash(input)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGenerateUUID(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GenerateUUID()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGenerateAPIKey(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GenerateAPIKey("")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGenerateToken(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GenerateToken(32)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGeneratePassword(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GeneratePassword(16, true, true, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHashPassword(b *testing.B) {
|
||||
password := "benchmark_password"
|
||||
salt := "benchmark_salt"
|
||||
for i := 0; i < b.N; i++ {
|
||||
HashPassword(password, salt)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user