完成基础功能
This commit is contained in:
2
go.mod
2
go.mod
@@ -3,6 +3,7 @@ module go-user-service
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.41.0
|
||||
google.golang.org/grpc v1.75.0
|
||||
google.golang.org/protobuf v1.36.8
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -19,7 +20,6 @@ require (
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
|
||||
14
go.sum
14
go.sum
@@ -49,21 +49,15 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
|
||||
@@ -17,5 +17,5 @@ type User struct {
|
||||
|
||||
// 实现 TableName 方法指定表名
|
||||
func (User) TableName() string {
|
||||
return "d_user"
|
||||
return "user"
|
||||
}
|
||||
|
||||
17
models/user_token.go
Normal file
17
models/user_token.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type UserToken struct {
|
||||
ID int8 `gorm:"id;primary_key"`
|
||||
UserId int8 `gorm:"UserId"`
|
||||
Token string `gorm:"token"`
|
||||
ExpirationTime time.Time `gorm:"expiration_time"`
|
||||
CreatedAt time.Time `gorm:"created_at;type:timestamptz"`
|
||||
UpdatedAt time.Time `gorm:"updated_at;type:timestamptz"`
|
||||
}
|
||||
|
||||
// 实现 TableName 方法指定表名
|
||||
func (UserToken) TableName() string {
|
||||
return "user_token"
|
||||
}
|
||||
@@ -11,7 +11,7 @@ message LoginRequest {
|
||||
|
||||
// 登录响应
|
||||
message LoginResponse {
|
||||
string user_id = 1;
|
||||
int64 user_id = 1;
|
||||
string username = 2;
|
||||
string email = 3;
|
||||
string access_token = 4;
|
||||
@@ -23,11 +23,13 @@ message RegisterRequest {
|
||||
string username = 1;
|
||||
string password = 2;
|
||||
string email = 3;
|
||||
string nickname = 4;
|
||||
string phone = 5;
|
||||
}
|
||||
|
||||
// 用户注册响应
|
||||
message RegisterResponse {
|
||||
string user_id = 1;
|
||||
int64 user_id = 1;
|
||||
string username = 2;
|
||||
string email = 3;
|
||||
}
|
||||
@@ -39,7 +41,7 @@ message ValidateTokenRequest {
|
||||
|
||||
// 令牌验证响应
|
||||
message ValidateTokenResponse {
|
||||
string user_id = 1;
|
||||
int64 user_id = 1;
|
||||
bool is_valid = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go-user-service/config"
|
||||
"go-user-service/libs"
|
||||
"go-user-service/models"
|
||||
userpb "go-user-service/proto/gen"
|
||||
"go-user-service/utils"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -14,11 +16,13 @@ import (
|
||||
|
||||
type UserService struct {
|
||||
userpb.UnimplementedUserServiceServer
|
||||
tokens map[string]string // token -> userID
|
||||
mu sync.RWMutex
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// 登录
|
||||
func (u *UserService) Login(ctx context.Context, req *userpb.LoginRequest) (*userpb.LoginResponse, error) {
|
||||
appConfig, _ := config.LoadConfig()
|
||||
Snowflake, _ := utils.NewSnowflake(appConfig.WorkerID, appConfig.DatacenterID)
|
||||
u.mu.RLock()
|
||||
defer u.mu.RUnlock()
|
||||
// 查询是否有这个用户名
|
||||
@@ -31,9 +35,84 @@ func (u *UserService) Login(ctx context.Context, req *userpb.LoginRequest) (*use
|
||||
hashPassword, _ := crypto.PasswordEncryption(user.Password + user.Salt)
|
||||
|
||||
if user.Password == hashPassword {
|
||||
|
||||
token, _ := crypto.GenerateStateToken(32)
|
||||
userId := Snowflake.NextID()
|
||||
db.Create(&models.UserToken{
|
||||
ID: int8(userId),
|
||||
UserId: user.ID,
|
||||
Token: token,
|
||||
ExpirationTime: time.Now().Add(24 * time.Hour),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
})
|
||||
return &userpb.LoginResponse{
|
||||
UserId: userId,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
AccessToken: token,
|
||||
ExpiresIn: 86400, // 1小时
|
||||
}, nil
|
||||
}
|
||||
return nil, status.Errorf(codes.Unauthenticated, "密码错误")
|
||||
}
|
||||
return nil, status.Errorf(codes.NotFound, "用户不存在")
|
||||
}
|
||||
|
||||
// 注册
|
||||
func (u *UserService) Register(ctx context.Context, req *userpb.RegisterRequest) (*userpb.RegisterResponse, error) {
|
||||
appConfig, _ := config.LoadConfig()
|
||||
Snowflake, _ := utils.NewSnowflake(appConfig.WorkerID, appConfig.DatacenterID)
|
||||
var crypto utils.Crypto
|
||||
u.mu.RLock()
|
||||
defer u.mu.RUnlock()
|
||||
// 查询是否有这个用户名
|
||||
var user models.User
|
||||
db := libs.GetDB()
|
||||
db.Model(&models.User{}).Where("username = ?", req.Username).First(&user)
|
||||
if user.Username == req.Username {
|
||||
return nil, status.Errorf(codes.AlreadyExists, "用户名已存在")
|
||||
}
|
||||
db.Model(&models.User{}).Where("email = ?", req.Email).First(&user)
|
||||
if user.Email == req.Email {
|
||||
return nil, status.Errorf(codes.AlreadyExists, "邮箱已存在")
|
||||
}
|
||||
userId := Snowflake.NextID()
|
||||
salt := crypto.GetRandString(10)
|
||||
hashPassword, _ := crypto.PasswordEncryption(req.Password + salt)
|
||||
db.Create(&models.User{
|
||||
ID: int8(userId),
|
||||
Nickname: req.Nickname,
|
||||
Username: req.Username,
|
||||
Password: hashPassword,
|
||||
Status: 1,
|
||||
Salt: salt,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
})
|
||||
return &userpb.RegisterResponse{
|
||||
UserId: userId,
|
||||
Username: req.Username,
|
||||
Email: req.Email,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 验证token信息
|
||||
func (u *UserService) ValidateToken(ctx context.Context, req *userpb.ValidateTokenRequest) (*userpb.ValidateTokenResponse, error) {
|
||||
u.mu.RLock()
|
||||
defer u.mu.RUnlock()
|
||||
// 查询是否有这个用户名
|
||||
var userToken models.UserToken
|
||||
var user models.User
|
||||
db := libs.GetDB()
|
||||
db.Model(&models.UserToken{}).Where("token = ? and expiration_time>?", req.AccessToken, time.Now()).First(&userToken)
|
||||
if userToken.Token != req.AccessToken {
|
||||
return &userpb.ValidateTokenResponse{
|
||||
IsValid: false,
|
||||
}, nil
|
||||
}
|
||||
db.Model(&models.User{}).Where("id = ?", userToken.UserId).First(&user)
|
||||
return &userpb.ValidateTokenResponse{
|
||||
UserId: int64(userToken.UserId),
|
||||
IsValid: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
randa "math/rand"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -54,3 +55,18 @@ func (*Crypto) GenerateStateToken(tokenLength int) (string, error) {
|
||||
token := base64.URLEncoding.EncodeToString(hash[:])
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (*Crypto) GetRandString(length int) string {
|
||||
// 初始化随机数种子
|
||||
randa.Seed(time.Now().UnixNano())
|
||||
|
||||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
// 方法1: 使用字节切片
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = letters[randa.Intn(len(letters))] // 随机选取一个字符
|
||||
}
|
||||
randomString := string(b)
|
||||
return randomString
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user