diff --git a/constant/auth.go b/constant/auth.go index 73ba01d..fdccb67 100644 --- a/constant/auth.go +++ b/constant/auth.go @@ -11,11 +11,3 @@ const ( UserRoleNormal = "normal" UserRoleAdmin = "admin" ) - -type HashAlgorithmType = string - -const ( - HashAlgorithmPBK2DF HashAlgorithmType = "pbkdf2_sha256" -) - -const PasswordHashIteration = 720000 diff --git a/go.mod b/go.mod index e6276cb..237aab8 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.22 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 + github.com/SJTU-jCourse/password_hasher v0.0.0-20240731144855-1f64f055ff5c github.com/bytedance/sonic v1.11.6 github.com/gin-gonic/contrib v0.0.0-20240508051311-c1c6bf0061b0 github.com/gin-gonic/gin v1.10.0 @@ -13,10 +14,10 @@ require ( github.com/hibiken/asynq v0.24.1 github.com/joho/godotenv v1.5.1 github.com/lib/pq v1.10.9 + github.com/mozillazg/go-pinyin v0.20.0 github.com/redis/go-redis/v9 v9.5.3 github.com/sashabaranov/go-openai v1.26.0 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.24.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gorm.io/driver/postgres v1.5.9 gorm.io/gorm v1.25.10 @@ -54,7 +55,6 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mozillazg/go-pinyin v0.20.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect @@ -62,9 +62,10 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/protobuf v1.34.2 // indirect diff --git a/go.sum b/go.sum index 696719b..e2c017d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/SJTU-jCourse/password_hasher v0.0.0-20240731144855-1f64f055ff5c h1:WGY3netUDrvhBsc0HOaorABwvTYjbbq8BG7Dyy9tbkk= +github.com/SJTU-jCourse/password_hasher v0.0.0-20240731144855-1f64f055ff5c/go.mod h1:vdxJOQaD9MexhM0evO8XgV8m+dkiXxA9M/WkOgeB3P4= github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04= github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= @@ -167,8 +169,8 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -189,8 +191,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/pkg/multi_level_cache/cache.go b/pkg/multi_level_cache/cache.go deleted file mode 100644 index 74a472c..0000000 --- a/pkg/multi_level_cache/cache.go +++ /dev/null @@ -1,76 +0,0 @@ -package multi_level_cache - -import ( - "context" - "log" - "time" - - "github.com/redis/go-redis/v9" -) - -// Cache 接口定义了缓存的基本操作 -type Cache interface { - Get(ctx context.Context, key string) (string, error) - MGet(ctx context.Context, keys []string) (map[string]string, error) - Set(ctx context.Context, key string, value string, expiration time.Duration) error - MSet(ctx context.Context, keys []string, values map[string]string, expiration time.Duration) error - Del(ctx context.Context, key string) error - MDel(ctx context.Context, keys []string) error -} - -// RedisCache 实现了 Cache 接口 -type RedisCache struct { - client *redis.Client -} - -func NewRedisCache(rdb *redis.Client) *RedisCache { - return &RedisCache{client: rdb} -} - -func (r *RedisCache) Get(ctx context.Context, key string) (string, error) { - return r.client.Get(ctx, key).Result() -} - -func (r *RedisCache) Set(ctx context.Context, key string, value string, expiration time.Duration) error { - return r.client.Set(ctx, key, value, expiration).Err() -} - -// FetchFunc 回源函数类型 -type FetchFunc = func(ctx context.Context, key string) (string, error) - -// MultiLevelCache 结构体,包含 Redis 和回源函数 -type MultiLevelCache struct { - redisCache Cache - fetchFunc FetchFunc - expiration time.Duration -} - -func NewMultiLevelCache(redisCache Cache, fetchFunc FetchFunc, expiration time.Duration) *MultiLevelCache { - return &MultiLevelCache{ - redisCache: redisCache, - fetchFunc: fetchFunc, - expiration: expiration, - } -} - -func (m *MultiLevelCache) Get(ctx context.Context, key string) (string, error) { - // 从 Redis 获取缓存 - value, err := m.redisCache.Get(ctx, key) - if err == nil { - return value, nil - } - - // 如果 Redis 缓存未命中,调用回源函数获取数据 - value, err = m.fetchFunc(ctx, key) - if err != nil { - return "", err - } - - // 将数据缓存到 Redis - err = m.redisCache.Set(ctx, key, value, m.expiration) - if err != nil { - log.Printf("Failed to set redis cache: %v", err) - } - - return value, nil -} diff --git a/pkg/password_hasher/hasher.go b/pkg/password_hasher/hasher.go deleted file mode 100644 index 8e102a9..0000000 --- a/pkg/password_hasher/hasher.go +++ /dev/null @@ -1,71 +0,0 @@ -package password_hasher - -import ( - "errors" - "fmt" - "os" - "strconv" - "strings" - - "jcourse_go/constant" -) - -type PasswordHasher interface { - HashPassword(password string, salt string, iteration int64) (string, error) -} - -var passwordHashers = map[constant.HashAlgorithmType]PasswordHasher{ - constant.HashAlgorithmPBK2DF: &PBK2DFSHA256PasswordHasher{}, -} - -func getPasswordHasher(algorithm constant.HashAlgorithmType) PasswordHasher { - passwordHasher, ok := passwordHashers[algorithm] - if ok { - return passwordHasher - } - return nil -} - -func MakeHashedPassword(rawPassword string, algorithm constant.HashAlgorithmType, salt string, iteration int64) (string, error) { - hasher := getPasswordHasher(algorithm) - if hasher == nil { - return "", errors.New("hash algorithm undefined") - } - return hasher.HashPassword(rawPassword, salt, iteration) -} - -func MakeHashedPasswordStore(rawPassword string) (string, error) { - salt := os.Getenv("HASH_SALT") - hash, err := MakeHashedPassword(rawPassword, constant.HashAlgorithmPBK2DF, salt, constant.PasswordHashIteration) - if err != nil { - return "", err - } - store := fmt.Sprintf("%s$%d$%s$%s", constant.HashAlgorithmPBK2DF, constant.PasswordHashIteration, salt, hash) - return store, nil -} - -func ValidatePassword(password, passwordStore string) (bool, error) { - val := strings.Split(passwordStore, "$") - if len(val) != 4 { - return false, nil - } - - iterations, err := strconv.ParseInt(val[1], 10, 64) - if err != nil { - return false, err - } - - expectedHash := val[3] - - actualHash, err := MakeHashedPassword(password, val[0], val[2], iterations) - - if err != nil { - return false, err - } - - if actualHash == expectedHash { - return true, nil - } - - return false, nil -} diff --git a/pkg/password_hasher/hasher_test.go b/pkg/password_hasher/hasher_test.go deleted file mode 100644 index e4e91f5..0000000 --- a/pkg/password_hasher/hasher_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package password_hasher - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMakeHashedPasswordStore(t *testing.T) { - store, err := MakeHashedPasswordStore("test") - assert.Nil(t, err) - assert.Equal(t, "pbkdf2_sha256$720000$$j92DYtoNAOL6uFf22YbNOKRJo8Q9a2gjXij5KkKhLPM=", store) -} - -func TestValidatePassword(t *testing.T) { - cases := []struct { - Name string - Password string - Store string - Want bool - }{ - {Name: "ok", Password: "test", Store: "pbkdf2_sha256$720000$$j92DYtoNAOL6uFf22YbNOKRJo8Q9a2gjXij5KkKhLPM=", Want: true}, - {Name: "not ok", Password: "test2", Store: "pbkdf2_sha256$720000$$j92DYtoNAOL6uFf22YbNOKRJo8Q9a2gjXij5KkKhLPM=", Want: false}, - {Name: "not ok", Password: "test", Store: "pbkdf2_sha256$72000$$j92DYtoNAOL6uFf22YbNOKRJo8Q9a2gjXij5KkKhLPM=", Want: false}, - {Name: "not ok", Password: "test", Store: "pbkdf2_sha256$720000$123$j92DYtoNAOL6uFf22YbNOKRJo8Q9a2gjXij5KkKhLPM=", Want: false}, - {Name: "not ok", Password: "test", Store: "pbkdf2_sha256$720000$$j2DYtoNAOL6uFf22YbNOKRJo8Q9a2gjXij5KkKhLPM=", Want: false}, - {Name: "not ok", Password: "test", Store: "", Want: false}, - } - for _, testcase := range cases { - t.Run(testcase.Name, func(t *testing.T) { - ok, err := ValidatePassword(testcase.Password, testcase.Store) - assert.Nil(t, err) - assert.Equal(t, testcase.Want, ok) - }) - } - -} diff --git a/pkg/password_hasher/pbk2df.go b/pkg/password_hasher/pbk2df.go deleted file mode 100644 index e745e7f..0000000 --- a/pkg/password_hasher/pbk2df.go +++ /dev/null @@ -1,19 +0,0 @@ -package password_hasher - -import ( - "crypto/sha256" - "encoding/base64" - - "golang.org/x/crypto/pbkdf2" -) - -type PBK2DFSHA256PasswordHasher struct { - salt string - iteration int64 -} - -func (h *PBK2DFSHA256PasswordHasher) HashPassword(password string, salt string, iteration int64) (string, error) { - hashed := pbkdf2.Key([]byte(password), []byte(salt), int(iteration), sha256.Size, sha256.New) - res := base64.StdEncoding.EncodeToString(hashed) - return res, nil -} diff --git a/service/auth.go b/service/auth.go index d55b857..a0293ca 100644 --- a/service/auth.go +++ b/service/auth.go @@ -8,10 +8,10 @@ import ( "math/big" "regexp" + "github.com/SJTU-jCourse/password_hasher" "jcourse_go/constant" "jcourse_go/model/converter" "jcourse_go/model/domain" - "jcourse_go/pkg/password_hasher" "jcourse_go/repository" "jcourse_go/rpc" )