diff --git a/go.mod b/go.mod index 2905189..2fc5343 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/redis/go-redis/v9 v9.5.3 github.com/samber/do v1.6.0 + github.com/spf13/cast v1.6.0 github.com/spf13/viper v1.19.0 go.uber.org/zap v1.27.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 @@ -58,7 +59,6 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/internal/models/user.go b/internal/models/user.go index a4280e2..6ab311a 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -4,3 +4,25 @@ type LoginInfo struct { Account string Pwd string } + +type AddInfo struct { + Account string `json:"account" binding:"required"` + Name string `json:"name" binding:"required"` + Mobile string `json:"mobile" binding:"required"` + Email string `json:"email" binding:"required"` + Sex int `json:"sex"` + Pwd string `json:"pwd" binding:"required"` +} + +type UserInfo struct { + Account string + Name string + Mobile string + Email string + Sex int +} + +type User struct { + Id uint + UserInfo +} diff --git a/internal/repo/user.go b/internal/repo/user.go index 8fd6987..db12d04 100644 --- a/internal/repo/user.go +++ b/internal/repo/user.go @@ -2,6 +2,7 @@ package repo import ( "context" + "fmt" "time" "github.com/samber/do" @@ -34,6 +35,10 @@ func (m *User) TableName() string { type UserRepo interface { GetUserByAccount(ctx context.Context, account string) (user User, err error) + GetUserByMobile(ctx context.Context, mobile string) (user User, err error) + GetUserByEmail(ctx context.Context, mobile string) (user User, err error) + SaveUser(ctx context.Context, user User) error + CreateUser(ctx context.Context, user *User) error } type userRepoS struct { @@ -50,3 +55,25 @@ func (u *userRepoS) GetUserByAccount(ctx context.Context, account string) (user err = u.db.Where("account = ?", account).Take(&user).Error return } + +func (u *userRepoS) GetUserByMobile(ctx context.Context, mobile string) (user User, err error) { + err = u.db.Where("mobile = ?", mobile).Take(&user).Error + return +} + +func (u *userRepoS) SaveUser(ctx context.Context, user User) error { + if user.ID <= 0 { + return fmt.Errorf("错误用户id:%d", user.ID) + } + + return u.db.Save(user).Error +} + +func (u *userRepoS) CreateUser(ctx context.Context, user *User) error { + return u.db.Create(user).Error +} + +func (u *userRepoS) GetUserByEmail(ctx context.Context, email string) (user User, err error) { + err = u.db.Where("email = ?", email).Take(&user).Error + return +} diff --git a/internal/service/user/interface.go b/internal/service/user/interface.go index 72b4cd7..f6119b1 100644 --- a/internal/service/user/interface.go +++ b/internal/service/user/interface.go @@ -6,6 +6,6 @@ import ( ) type UserService interface { - Add() error + Add(ctx context.Context, info *models.AddInfo) (id uint, err error) Login(ctx context.Context, lInfo models.LoginInfo) error } diff --git a/internal/service/user/user.go b/internal/service/user/user.go index d31a4eb..f34b72d 100644 --- a/internal/service/user/user.go +++ b/internal/service/user/user.go @@ -5,14 +5,12 @@ import ( "busniess-user-center/internal/models" "busniess-user-center/internal/repo" "busniess-user-center/pkg/redis" + contextUtil "busniess-user-center/pkg/utils/context" + stringUtil "busniess-user-center/pkg/utils/string" "busniess-user-center/pkg/utils/token" "context" "fmt" - "github.com/gin-gonic/gin" - "github.com/golang-module/dongle" - - "github.com/dgrijalva/jwt-go" "github.com/samber/do" "go.uber.org/zap" "gorm.io/gorm" @@ -22,6 +20,7 @@ const ( COOKIE_KEY_TOKEN = "token" COOKIE_KEY_ACCOUNT = "account" COOKIE_KEY_ID = "id" + saltLen = 8 ) func init() { @@ -46,8 +45,59 @@ func NewUserService(i *do.Injector) (UserService, error) { }, nil } -func (u *userService) Add() error { - return nil +func (u *userService) Add(ctx context.Context, info *models.AddInfo) (id uint, err error) { + session, err := contextUtil.GetSession(ctx) + if err != nil { + return + } + + // 判断手机号 + _, err = u.repo.GetUserByAccount(ctx, info.Account) + if err != nil && err != gorm.ErrRecordNotFound { + return + } + + if err == nil { + err = fmt.Errorf("用户%s已经存在", info.Account) + return + } + + // 判断账号,手机号是否唯一 + mUser, err := u.repo.GetUserByMobile(ctx, info.Mobile) + if err != nil && err != gorm.ErrRecordNotFound { + return + } + + if err == nil { + err = fmt.Errorf("手机号:%s的用户%s已经存在", info.Mobile, mUser.Account) + return + } + + // 邮箱 + eUser, err := u.repo.GetUserByEmail(ctx, info.Email) + if err != nil && err != gorm.ErrRecordNotFound { + return + } + + if err == nil { + err = fmt.Errorf("邮箱:%s的用户%s已经存在", info.Mobile, eUser.Account) + } + + salt := stringUtil.RandStringRunes(saltLen) + pwd := u.sha256(info.Pwd, salt) + user := repo.User{ + Account: info.Account, + Name: info.Name, + Mobile: info.Mobile, + Email: info.Email, + Pwd: pwd, + Sex: info.Sex, + CreatedBy: session.Account, + } + + err = u.repo.CreateUser(ctx, &user) + id = user.ID + return } func (u *userService) Login(ctx context.Context, info models.LoginInfo) error { @@ -72,35 +122,45 @@ func (u *userService) Login(ctx context.Context, info models.LoginInfo) error { return u.setLoginStatus(ctx, user, claims) } -func (u *userService) sha256(pwd string, salt string) string { - fromStr := fmt.Sprintf("%s:%s", pwd, salt) - return dongle.Encrypt.FromString(fromStr).BySha256().ToHexString() -} - -func (u *userService) getToken(claims jwt.MapClaims) (string, error) { - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenStr, err := token.SignedString([]byte(u.conf.Jwt.Secret)) - return tokenStr, err -} - -func (u *userService) setLoginStatus(ctx context.Context, user repo.User, claims jwt.MapClaims) error { - tokenStr, err := u.getToken(claims) - if err != nil { - return err - } - - // 只做简单的token记录,校验时如果没有从reids获取到登陆信息,返回失败 - err = u.tokenRefresher.SetUseridTokenRelation(user.ID, tokenStr) - if err != nil { - return fmt.Errorf("设置redis失败:%s", err.Error()) - } - - c := ctx.(*gin.Context) - expires := u.conf.Jwt.Expires - domain := u.conf.App.Host - c.Writer.Header().Add("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%d; Path=/;Domain=%s", COOKIE_KEY_TOKEN, tokenStr, expires, domain)) - c.Writer.Header().Add("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%d; Path=/;Domain=%s", COOKIE_KEY_ACCOUNT, claims["account"], expires, domain)) - c.Writer.Header().Add("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%d; Path=/;Domain=%s", COOKIE_KEY_ID, claims["id"], expires, domain)) - +func (u *userService) LoginOut() error { + // 获取当前用户信息 + // 删除redis缓存 + // remome cookie + return nil +} + +func (u *userService) Modify() error { + // 获取当前操作用户 + // 判断修改用户是否是同一个人 + return nil +} + +func (u *userService) Disable() error { + // 获取操作用户 + // 判断是否有权限 + // 修改对应用户状态 + return nil +} + +func (u *userService) Able() error { + // 获取操作用户 + // 判断是否有权限 + // 修改对应用户状态 + return nil +} + +func (u *userService) Users() error { + // 获取操作用户 + // 判断是否有权限 + // 返回用户列表 + return nil +} + +func (u *userService) ResetPwd() error { + // 获取操作用户 + // 判断是否本人操作 + // 判断当前密码是否正确 + // 生成新的密码并保存 + // 删除旧的缓存 return nil } diff --git a/internal/service/user/util.go b/internal/service/user/util.go index d188691..98a5b1b 100644 --- a/internal/service/user/util.go +++ b/internal/service/user/util.go @@ -2,9 +2,13 @@ package user import ( "busniess-user-center/internal/repo" + "context" + "fmt" "time" "github.com/dgrijalva/jwt-go" + "github.com/gin-gonic/gin" + "github.com/golang-module/dongle" ) func creteLoginTokenClaims(user *repo.User, expire int) jwt.MapClaims { @@ -19,3 +23,36 @@ func creteLoginTokenClaims(user *repo.User, expire int) jwt.MapClaims { return userTokenClaims } + +func (u *userService) sha256(pwd string, salt string) string { + fromStr := fmt.Sprintf("%s:%s", pwd, salt) + return dongle.Encrypt.FromString(fromStr).BySha256().ToHexString() +} + +func (u *userService) getToken(claims jwt.MapClaims) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenStr, err := token.SignedString([]byte(u.conf.Jwt.Secret)) + return tokenStr, err +} + +func (u *userService) setLoginStatus(ctx context.Context, user repo.User, claims jwt.MapClaims) error { + tokenStr, err := u.getToken(claims) + if err != nil { + return err + } + + // 只做简单的token记录,校验时如果没有从reids获取到登陆信息,返回失败 + err = u.tokenRefresher.SetUseridTokenRelation(user.ID, tokenStr) + if err != nil { + return fmt.Errorf("设置redis失败:%s", err.Error()) + } + + c := ctx.(*gin.Context) + expires := u.conf.Jwt.Expires + domain := u.conf.App.Host + c.Writer.Header().Add("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%d; Path=/;Domain=%s", COOKIE_KEY_TOKEN, tokenStr, expires, domain)) + c.Writer.Header().Add("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%d; Path=/;Domain=%s", COOKIE_KEY_ACCOUNT, claims["account"], expires, domain)) + c.Writer.Header().Add("Set-Cookie", fmt.Sprintf("%s=%s; Max-Age=%d; Path=/;Domain=%s", COOKIE_KEY_ID, claims["id"], expires, domain)) + + return nil +} diff --git a/pkg/middleware/jwt/jwt.go b/pkg/middleware/jwt/jwt.go index deec6dd..c11abcf 100644 --- a/pkg/middleware/jwt/jwt.go +++ b/pkg/middleware/jwt/jwt.go @@ -11,7 +11,7 @@ import ( jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" - + "github.com/spf13/cast" "go.uber.org/zap" ) @@ -75,7 +75,7 @@ func NewJwtAuthMiddleware(logger *zap.SugaredLogger, refresher *token.TokenRefre idInterface, ok := claims[JwtIdentityKey] if ok { - id = idInterface.(uint) + id = cast.ToUint(idInterface) } // 从redis获取 @@ -108,6 +108,7 @@ func NewJwtAuthMiddleware(logger *zap.SugaredLogger, refresher *token.TokenRefre }, Authorizator: func(data interface{}, c *gin.Context) bool { if v, ok := data.(*session.Session); ok { + _ = contextUtil.PutSession(c, v) c.Request = c.Request.WithContext(contextUtil.PutSession(c.Request.Context(), v)) return v.ID != 0 } diff --git a/pkg/utils/context/context.go b/pkg/utils/context/context.go index 9a5d8ec..313531b 100644 --- a/pkg/utils/context/context.go +++ b/pkg/utils/context/context.go @@ -6,6 +6,12 @@ import ( pkgErrors "busniess-user-center/pkg/errors" "busniess-user-center/pkg/utils/session" + + "github.com/gin-gonic/gin" +) + +const ( + JwtIdentityKey = "id" ) type contextSessionKey struct{} @@ -32,18 +38,34 @@ func GetSessionUserID(ctx context.Context) (userID uint, err error) { if err != nil { return 0, errors.New(pkgErrors.UNAUTH) } + return sess.ID, nil } func GetSession(ctx context.Context) (sess *session.Session, err error) { - sess, ok := ctx.Value(contextSessionKey{}).(*session.Session) - if ok { - return + switch c := ctx.(type) { + case *gin.Context: + if sessionI, ok := c.Get(JwtIdentityKey); ok { + if sess, ok = sessionI.(*session.Session); ok { + return sess, nil + } + } + + default: + if sess, ok := ctx.Value(contextSessionKey{}).(*session.Session); ok { + return sess, nil + } } return nil, errors.New(pkgErrors.UNAUTH) } -func PutSession(ctx context.Context, sess *session.Session) (newCtx context.Context) { - return context.WithValue(ctx, contextSessionKey{}, sess) +func PutSession(ctx context.Context, sess *session.Session) context.Context { + switch c := ctx.(type) { + case *gin.Context: + c.Set(JwtIdentityKey, sess) + return ctx + default: + return context.WithValue(ctx, contextSessionKey{}, sess) + } } diff --git a/pkg/utils/string/string.go b/pkg/utils/string/string.go new file mode 100644 index 0000000..6862c21 --- /dev/null +++ b/pkg/utils/string/string.go @@ -0,0 +1,22 @@ +package string + +import ( + "math/rand" + "time" +) + +var ( + letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func RandStringRunes(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} diff --git a/server/user/user.go b/server/user/user.go index 00ec66b..64e4b0f 100644 --- a/server/user/user.go +++ b/server/user/user.go @@ -3,6 +3,7 @@ package user import ( "busniess-user-center/internal/models" "busniess-user-center/internal/service/user" + ginUtil "busniess-user-center/pkg/utils/gin" "busniess-user-center/server/user/proto" "context" @@ -34,20 +35,22 @@ func RegisterRoute(api *gin.RouterGroup) { api.POST("/login", ginUtil.WrapNoRsp(server.Login)) } -func (u *UserServer) Add(ctx context.Context, req *proto.AddRequst) (rsp proto.AddResponse, err error) { +func (u *UserServer) Add(ctx context.Context, req *models.AddInfo) (rsp proto.AddResponse, err error) { // 转换dto - err = u.userService.Add() + id, err := u.userService.Add(ctx, req) if err != nil { u.logger.Errorf("add user err:", err.Error()) return } - rsp.Id = 1 - rsp.Account = "test" + rsp.Id = id + rsp.Account = req.Account + rsp.Name = req.Name + return } -func (u *UserServer) Login(ctx context.Context, req *proto.LoginRequest) (err error) { +func (u *UserServer) Login(ctx context.Context, req *models.LoginInfo) (err error) { // 转换dto info := models.LoginInfo{ Account: req.Account,