Jelajahi Sumber

阶段提交

yjp 1 tahun lalu
induk
melakukan
5e6abf0b4f

+ 143 - 2
convenient/domain/auth/auth.go

@@ -7,6 +7,16 @@ import (
 	"git.sxidc.com/go-framework/baize/convenient/domain/auth/role"
 	"git.sxidc.com/go-framework/baize/convenient/domain/auth/user"
 	"git.sxidc.com/go-framework/baize/framework/binding"
+	"git.sxidc.com/go-framework/baize/framework/core/api"
+	"git.sxidc.com/go-framework/baize/framework/core/api/request"
+	"git.sxidc.com/go-framework/baize/framework/core/api/response"
+	"git.sxidc.com/go-framework/baize/framework/core/domain"
+	"git.sxidc.com/go-framework/baize/framework/core/domain/entity"
+	"git.sxidc.com/go-framework/baize/framework/core/infrastructure"
+	"git.sxidc.com/go-framework/baize/framework/core/infrastructure/database"
+	"git.sxidc.com/go-framework/baize/framework/core/infrastructure/database/sql"
+	"git.sxidc.com/go-tools/utils/encoding"
+	"github.com/pkg/errors"
 )
 
 // Simple Bind参数
@@ -16,6 +26,12 @@ type Simple struct {
 
 	// AES加密用到的Key
 	AESKey string
+
+	// JWT的Key
+	JWTSecretKey string
+
+	// JWT到期时间
+	JWTExpiredSec int64
 }
 
 func (simple *Simple) bind(binder *binding.Binder) {
@@ -25,9 +41,134 @@ func (simple *Simple) bind(binder *binding.Binder) {
 	(&user.Simple{Schema: simple.Schema, AESKey: simple.AESKey}).Bind(binder)
 	(&relations.Simple{Schema: simple.Schema}).Bind(binder)
 
-	// TODO Challenge
+	// 登录
+	binding.PostBind(binder, &binding.SimpleBindItem[map[string]any]{
+		Path:             "/login",
+		SendResponseFunc: response.SendMapResponse,
+		RequestParams:    &LoginJsonBody{},
+		ServiceFunc: func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) (map[string]any, error) {
+			errResponse := map[string]any{
+				"token": "",
+			}
+
+			jsonBody, err := request.ToConcrete[*LoginJsonBody](params)
+			if err != nil {
+				return errResponse, err
+			}
+
+			encryptedPassword, err := encoding.AESEncrypt(jsonBody.Password, simple.AESKey)
+			if err != nil {
+				return errResponse, errors.New(err.Error())
+			}
+
+			userTableName := domain.TableName(simple.Schema, &user.Entity{})
+
+			dbExecutor := i.DBExecutor()
+
+			result, err := database.QueryOne(dbExecutor, &sql.QueryOneExecuteParams{
+				TableName: userTableName,
+				Conditions: sql.NewConditions().
+					Equal(user.ColumnUserName, jsonBody.UserName).
+					Equal(user.ColumnPassword, encryptedPassword),
+			})
+			if err != nil {
+				if database.IsErrorDBRecordNotExist(err) {
+					return errResponse, errors.New("用户名或密码错误")
+				}
+
+				return errResponse, errors.New(err.Error())
+			}
+
+			existUser := new(user.Entity)
+			err = sql.ParseSqlResult(result, existUser)
+			if err != nil {
+				return errResponse, err
+			}
+
+			token, err := newJWT(simple.JWTSecretKey, existUser.ID, simple.JWTExpiredSec)
+			if err != nil {
+				return errResponse, errors.New(err.Error())
+			}
+
+			err = database.Update(dbExecutor, &sql.UpdateExecuteParams{
+				TableName:  userTableName,
+				TableRow:   sql.NewTableRow().Add(user.ColumnToken, token),
+				Conditions: sql.NewConditions().Equal(entity.ColumnID, existUser.ID),
+			})
+			if err != nil {
+				return errResponse, errors.New(err.Error())
+			}
+
+			return map[string]any{
+				"token": token,
+			}, nil
+		},
+	}, Authentication())
 
-	// TODO 生成Token
+	// 注销
+	binding.PostBind(binder, &binding.SimpleBindItem[any]{
+		Path:             "/logout",
+		SendResponseFunc: response.SendMsgResponse,
+		ServiceFunc: func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+			errResponse := map[string]any{
+				"token": "",
+			}
+
+			jsonBody, err := request.ToConcrete[*LoginJsonBody](params)
+			if err != nil {
+				return errResponse, err
+			}
+
+			encryptedPassword, err := encoding.AESEncrypt(jsonBody.Password, simple.AESKey)
+			if err != nil {
+				return errResponse, errors.New(err.Error())
+			}
+
+			userTableName := domain.TableName(simple.Schema, &user.Entity{})
+
+			dbExecutor := i.DBExecutor()
+
+			result, err := database.QueryOne(dbExecutor, &sql.QueryOneExecuteParams{
+				TableName: userTableName,
+				Conditions: sql.NewConditions().
+					Equal(user.ColumnUserName, jsonBody.UserName).
+					Equal(user.ColumnPassword, encryptedPassword),
+			})
+			if err != nil {
+				if database.IsErrorDBRecordNotExist(err) {
+					return errResponse, errors.New("用户名或密码错误")
+				}
+
+				return errResponse, errors.New(err.Error())
+			}
+
+			existUser := new(user.Entity)
+			err = sql.ParseSqlResult(result, existUser)
+			if err != nil {
+				return errResponse, err
+			}
+
+			token, err := newJWT(simple.JWTSecretKey, existUser.ID, simple.JWTExpiredSec)
+			if err != nil {
+				return errResponse, errors.New(err.Error())
+			}
+
+			err = database.Update(dbExecutor, &sql.UpdateExecuteParams{
+				TableName:  userTableName,
+				TableRow:   sql.NewTableRow().Add(user.ColumnToken, token),
+				Conditions: sql.NewConditions().Equal(entity.ColumnID, existUser.ID),
+			})
+			if err != nil {
+				return errResponse, errors.New(err.Error())
+			}
+
+			return map[string]any{
+				"token": token,
+			}, nil
+		},
+	})
+
+	// TODO Challenge
 }
 
 func BindAuth(binder *binding.Binder, simple *Simple) {

+ 60 - 0
convenient/domain/auth/jwt.go

@@ -0,0 +1,60 @@
+package auth
+
+import (
+	"errors"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/pkg/errors"
+	"time"
+)
+
+func newJWT(jwtSecretKey string, userID string, expSec int64) (string, error) {
+	token := jwt.New(jwt.SigningMethodHS256)
+	claims := make(jwt.MapClaims)
+
+	if expSec > 0 {
+		claims["exp"] = time.Now().Add(time.Duration(expSec) * time.Second).Unix()
+	}
+
+	claims["aud"] = userID
+	claims["iat"] = time.Now().Unix()
+	token.Claims = claims
+
+	tokenString, err := token.SignedString([]byte(jwtSecretKey))
+	if err != nil {
+		return "", errors.New(err.Error())
+	}
+
+	return tokenString, nil
+}
+
+func checkJWT(jwtSecretKey string, tokenStr string) (bool, string, error) {
+	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
+		return []byte(jwtSecretKey), nil
+	})
+	if err != nil {
+		var validationErr *jwt.ValidationError
+
+		ok := errors.As(err, &validationErr)
+		if !ok {
+			return false, "", errors.New(err.Error())
+		}
+
+		if validationErr.Errors == jwt.ValidationErrorExpired {
+			return false, "", nil
+		}
+
+		return false, "", errors.New(err.Error())
+	}
+
+	claims, ok := token.Claims.(jwt.MapClaims)
+	if !ok {
+		return false, "", errors.New("类型转换失败")
+	}
+
+	userID, ok := claims["aud"].(string)
+	if !ok {
+		return false, "", errors.New("类型转换失败")
+	}
+
+	return token.Valid, userID, nil
+}

+ 14 - 0
convenient/domain/auth/middleware.go

@@ -3,6 +3,11 @@ package auth
 import (
 	"git.sxidc.com/go-framework/baize/framework/core/api"
 	"git.sxidc.com/go-framework/baize/framework/core/infrastructure"
+	"github.com/pkg/errors"
+)
+
+const (
+	tokenContextKey = "auth-context-token"
 )
 
 func Authentication(i *infrastructure.Infrastructure) api.Handler {
@@ -12,3 +17,12 @@ func Authentication(i *infrastructure.Infrastructure) api.Handler {
 		c.Next()
 	}
 }
+
+func GetTokenContext(c *api.Context) (string, error) {
+	value, exist := c.Get(tokenContextKey)
+	if !exist {
+		return "", errors.New("没有找到token")
+	}
+
+	return value.(string), nil
+}

+ 8 - 0
convenient/domain/auth/request_params.go

@@ -0,0 +1,8 @@
+package auth
+
+type (
+	LoginJsonBody struct {
+		UserName string `json:"userName" binding:"required"`
+		Password string `json:"password" binding:"required"`
+	}
+)

+ 68 - 8
convenient/domain/auth/user/api.go

@@ -3,6 +3,7 @@ package user
 import (
 	"git.sxidc.com/go-framework/baize/convenient/entity_crud"
 	"git.sxidc.com/go-framework/baize/framework/binding"
+	"git.sxidc.com/go-framework/baize/framework/core/api/response"
 	"git.sxidc.com/go-framework/baize/framework/core/domain"
 	"git.sxidc.com/go-framework/baize/framework/core/domain/entity"
 	"git.sxidc.com/go-framework/baize/framework/core/infrastructure"
@@ -41,7 +42,7 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 
 			encryptedPassword, err := encoding.AESEncrypt(userEntity.Password, simple.AESKey)
 			if err != nil {
-				return err
+				return errors.New(err.Error())
 			}
 
 			userEntity.Password = encryptedPassword
@@ -52,14 +53,14 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 
 			encryptedPhone, err := encoding.AESEncrypt(userEntity.Phone, simple.AESKey)
 			if err != nil {
-				return err
+				return errors.New(err.Error())
 			}
 
 			userEntity.Phone = encryptedPhone
 
 			encryptedEmail, err := encoding.AESEncrypt(userEntity.Email, simple.AESKey)
 			if err != nil {
-				return err
+				return errors.New(err.Error())
 			}
 
 			userEntity.Email = encryptedEmail
@@ -98,7 +99,7 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 			if strutils.IsStringNotEmpty(userEntity.Password) {
 				encryptedPassword, err := encoding.AESEncrypt(userEntity.Password, simple.AESKey)
 				if err != nil {
-					return err
+					return errors.New(err.Error())
 				}
 
 				userEntity.Password = encryptedPassword
@@ -111,7 +112,7 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 			if strutils.IsStringNotEmpty(userEntity.Phone) {
 				encryptedPhone, err := encoding.AESEncrypt(userEntity.Phone, simple.AESKey)
 				if err != nil {
-					return err
+					return errors.New(err.Error())
 				}
 
 				userEntity.Phone = encryptedPhone
@@ -120,7 +121,7 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 			if strutils.IsStringNotEmpty(userEntity.Email) {
 				encryptedEmail, err := encoding.AESEncrypt(userEntity.Email, simple.AESKey)
 				if err != nil {
-					return err
+					return errors.New(err.Error())
 				}
 
 				userEntity.Email = encryptedEmail
@@ -195,7 +196,7 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 			if strutils.IsStringNotEmpty(phone) {
 				encryptedPassword, err := encoding.AESEncrypt(phone, simple.AESKey)
 				if err != nil {
-					return false, err
+					return false, errors.New(err.Error())
 				}
 
 				conditions.Equal(ColumnPassword, encryptedPassword)
@@ -211,7 +212,7 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 			if strutils.IsStringNotEmpty(email) {
 				encryptedPassword, err := encoding.AESEncrypt(email, simple.AESKey)
 				if err != nil {
-					return false, err
+					return false, errors.New(err.Error())
 				}
 
 				conditions.Equal(ColumnPassword, encryptedPassword)
@@ -221,5 +222,64 @@ func (simple *Simple) Bind(binder *binding.Binder) {
 		default:
 			return false, nil
 		}
+	}), entity_crud.WithQueryCallbacks(&entity_crud.QueryCallbacks[Info]{
+		OnSuccessReturn: func(e entity.Entity, i *infrastructure.Infrastructure, output response.InfosData[Info]) (response.InfosData[Info], error) {
+			errResponse := response.InfosData[Info]{
+				Infos: make([]Info, 0),
+			}
+
+			retInfos := make([]Info, len(output.Infos))
+			infos := output.Infos
+
+			for index, info := range infos {
+				if strutils.IsStringNotEmpty(info.Name) {
+					encryptedName, err := encoding.AESEncrypt(info.Name, simple.AESKey)
+					if err != nil {
+						return errResponse, errors.New(err.Error())
+					}
+
+					info.Name = encryptedName
+				}
+
+				if strutils.IsStringNotEmpty(info.Email) {
+					encryptedEmail, err := encoding.AESEncrypt(info.Email, simple.AESKey)
+					if err != nil {
+						return errResponse, errors.New(err.Error())
+					}
+
+					info.Email = encryptedEmail
+				}
+
+				retInfos[index] = info
+			}
+
+			return response.InfosData[Info]{
+				Infos:      retInfos,
+				TotalCount: output.TotalCount,
+				PageNo:     output.PageNo,
+			}, nil
+		},
+	}), entity_crud.WithGetByIDCallbacks(&entity_crud.GetByIDCallbacks[Info]{
+		OnSuccessReturn: func(e entity.Entity, i *infrastructure.Infrastructure, output Info) (Info, error) {
+			if strutils.IsStringNotEmpty(output.Name) {
+				encryptedName, err := encoding.AESEncrypt(output.Name, simple.AESKey)
+				if err != nil {
+					return Info{}, errors.New(err.Error())
+				}
+
+				output.Name = encryptedName
+			}
+
+			if strutils.IsStringNotEmpty(output.Email) {
+				encryptedEmail, err := encoding.AESEncrypt(output.Email, simple.AESKey)
+				if err != nil {
+					return Info{}, errors.New(err.Error())
+				}
+
+				output.Email = encryptedEmail
+			}
+
+			return output, nil
+		},
 	}))
 }

+ 6 - 3
convenient/domain/auth/user/entity.go

@@ -12,9 +12,10 @@ import (
 const (
 	FieldUserName      = "UserName"
 	FieldPassword      = "Password"
-	FieldName          = "name"
-	FieldPhone         = "phone"
-	FieldEmail         = "email"
+	FieldName          = "Name"
+	FieldPhone         = "Phone"
+	FieldEmail         = "Email"
+	FieldToken         = "Token"
 	FieldLastLoginTime = "LastLoginTime"
 )
 
@@ -24,6 +25,7 @@ var (
 	ColumnName          = domain.ColumnName(FieldName)
 	ColumnPhone         = domain.ColumnName(FieldPhone)
 	ColumnEmail         = domain.ColumnName(FieldEmail)
+	ColumnToken         = domain.ColumnName(FieldToken)
 	ColumnLastLoginTime = domain.ColumnName(FieldLastLoginTime)
 )
 
@@ -33,6 +35,7 @@ var fieldMap = map[string]string{
 	FieldName:          "姓名",
 	FieldPhone:         "手机号",
 	FieldEmail:         "邮箱",
+	FieldToken:         "token",
 	FieldLastLoginTime: "最近登录时间",
 }
 

+ 0 - 1
convenient/domain/auth/user/info.go

@@ -7,7 +7,6 @@ import (
 type Info struct {
 	application.InfoIDField
 	UserName      string `json:"userName" sqlresult:"column:user_name"`
-	Password      string `json:"password" sqlresult:"column:password"`
 	Name          string `json:"name" sqlresult:"column:name"`
 	Phone         string `json:"phone" sqlresult:"column:phone"`
 	Email         string `json:"email" sqlresult:"column:email"`

+ 1 - 0
go.mod

@@ -5,6 +5,7 @@ go 1.22.3
 require (
 	git.sxidc.com/go-tools/utils v1.5.15
 	git.sxidc.com/service-supports/websocket v1.3.1
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/gin-gonic/gin v1.10.0
 	github.com/go-playground/locales v0.14.1
 	github.com/go-playground/universal-translator v0.18.1

+ 2 - 0
go.sum

@@ -25,6 +25,8 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=