diff --git a/database/admindb.sql b/database/admindb.sql new file mode 100644 index 00000000..0e44b85a --- /dev/null +++ b/database/admindb.sql @@ -0,0 +1,58 @@ +drop table if exists t_users; +CREATE TABLE t_users ( +id INT AUTO_INCREMENT PRIMARY KEY, +username VARCHAR(255) NOT NULL UNIQUE, +password VARCHAR(255) NOT NULL +); + +drop table if exists t_roles; +CREATE TABLE t_roles ( +id INT AUTO_INCREMENT PRIMARY KEY, +name VARCHAR(255) NOT NULL UNIQUE +); + +CREATE TABLE permissions ( +id INT AUTO_INCREMENT PRIMARY KEY, +name VARCHAR(255) NOT NULL UNIQUE +); + +drop table if exists t_permissions; +CREATE TABLE t_permissions ( +id INT AUTO_INCREMENT PRIMARY KEY, +name VARCHAR(255) NOT NULL UNIQUE +); + +drop table if exists t_user_roles; +CREATE TABLE t_user_roles ( +id INT AUTO_INCREMENT PRIMARY KEY, +user_id INT, +role_id INT +); + +drop table if exists t_role_permissions; +CREATE TABLE t_role_permissions ( +id INT AUTO_INCREMENT PRIMARY KEY, +role_id INT, +permission_id INT +); + +drop table if exists t_actions; +create table t_actions +( +id int AUTO_INCREMENT PRIMARY KEY, +name VARCHAR(255) NOT NULL UNIQUE +); + +drop table if exists t_sessions; +create table if not exists t_sessions ( +id bigint, +username varchar(255) not null default "", +refresh_token varchar(255) not null default "", +client_ip varchar(255) not null default "", +user_agent varchar(255) NOT NULL default "", +is_blocked tinyint not null default "0", +expires_at int not null default "0", +created_at int not null default "0", +primary key id(id), +unique key username(username) +); diff --git a/server/adminserver/api/middleware.go b/server/adminserver/api/middleware.go index 006a3d20..ec4a3419 100644 --- a/server/adminserver/api/middleware.go +++ b/server/adminserver/api/middleware.go @@ -12,8 +12,8 @@ import ( const ( authorizationHeaderKey = "authorization" - authorizationTypeBearer = "bearer" authorizationTypeBasic = "basic" + authorizationTypeBearer = "bearer" authorizationPayloadKey = "authorization_payload" ) @@ -33,11 +33,11 @@ func authMiddleware(tokenMaker token.Maker) gin.HandlerFunc { return } + // Use http basic // Headers key: Authorization // Headers value: Basic cm9vdDoxMjM0NTY= authorizationType := strings.ToLower(fields[0]) if authorizationType == authorizationTypeBasic { - user, password, hasAuth := ctx.Request.BasicAuth() if hasAuth && user == "root" && password == "123456" { ctx.Next() @@ -45,6 +45,7 @@ func authMiddleware(tokenMaker token.Maker) gin.HandlerFunc { } } + // Use accounts from db if authorizationType != authorizationTypeBearer { err := fmt.Errorf("unsupported authorization type %s", authorizationType) ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err)) diff --git a/server/adminserver/api/server.go b/server/adminserver/api/server.go index 5e2d5ee0..1e5d784c 100644 --- a/server/adminserver/api/server.go +++ b/server/adminserver/api/server.go @@ -1,6 +1,7 @@ package api import ( + "fmt" "github.com/gin-gonic/gin" "github.com/jmoiron/sqlx" "main/token" @@ -13,9 +14,26 @@ type Server struct { router *gin.Engine } +/* + +Install +go get github.com/jmoiron/sqlx +go get -u github.com/golang-jwt/jwt/v5 +go get -u github.com/o1egl/paseto + +*/ + +var TokenSymmetricKey = "12345678901234567890123456789012" + func NewServer(store *sqlx.DB) (*Server, error) { + tokenMaker, err := token.NewPasetoMaker(TokenSymmetricKey) + if err != nil { + return nil, fmt.Errorf("cannot create token maker: %w", err) + } + server := &Server{ - store: store, + store: store, + tokenMaker: tokenMaker, } server.setupRouter() @@ -26,12 +44,13 @@ func (server *Server) setupRouter() { router := gin.Default() // Default routers router.GET("/welcome", server.welcome) + router.POST("/create_user", server.createUser) router.POST("/login", server.loginUser) // Authentication routers authRoutes := router.Group("/").Use(authMiddleware(server.tokenMaker)) - authRoutes.GET("/accounts", server.listAccounts) - authRoutes.GET("/guilds", server.listGuilds) + authRoutes.GET("/users", server.listUsers) + authRoutes.GET("/users/:user_id", server.getUser) authRoutes.GET("/guilds/:guild_id", server.getGuild) authRoutes.POST("/guilds", server.createGuild) authRoutes.DELETE("/guilds/:guild_id", server.deleteGuild) diff --git a/server/adminserver/api/user.go b/server/adminserver/api/user.go index c6c38c7a..fa88c1dd 100644 --- a/server/adminserver/api/user.go +++ b/server/adminserver/api/user.go @@ -8,18 +8,126 @@ import ( ) type createUserRequest struct { - Username string `json:"username" binding:"required,alphanum"` + Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required,min=6"` - FullName string `json:"full_name" binding:"required"` - Email string `json:"email" binding:"required,email"` +} +type loginUserRequest struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required,min=6"` +} + +type loginUserResponse struct { + SessionID int64 `json:"session_id"` + AccessToken string `json:"access_token"` + AccessTokenExpiresAt time.Time `json:"access_token_expires_at"` + RefreshToken string `json:"refresh_token"` + RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"` + User userResponse `json:"user"` } type userResponse struct { - Username string `json:"username"` - FullName string `json:"full_name"` - Email string `json:"email"` - PasswordChangedAt time.Time `json:"password_changed_at"` - CreatedAt time.Time `json:"created_at"` + Username string `json:"username"` +} + +func newUserResponse(user *model.User) userResponse { + return userResponse{ + Username: user.Username, + } +} + +type CreateUserParams struct { + Username string `json:"username"` + Password string `json:"password"` +} + +func (server *Server) welcome(ctx *gin.Context) { + ctx.JSON(http.StatusOK, "welcome") +} + +func (server *Server) createUser(ctx *gin.Context) { + var req createUserRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + ctx.JSON(http.StatusBadRequest, errorResponse(err)) + return + } + + var createUserParams model.User + createUserParams.Username = req.Username + createUserParams.Password = req.Password + + userId, err := model.CreateUser(&createUserParams) + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"}) + return + } + + ctx.JSON(http.StatusCreated, gin.H{"userId": userId, "userName": createUserParams.Username}) +} + +func (server *Server) loginUser(ctx *gin.Context) { + var req loginUserRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + ctx.JSON(http.StatusBadRequest, errorResponse(err)) + return + } + + user, err := model.GetUserByUsername(req.Username) + if err != nil { + ctx.JSON(http.StatusInternalServerError, errorResponse(err)) + return + } + if !CheckPassword(req.Password, user.Password) { + ctx.JSON(http.StatusUnauthorized, errorResponse(err)) + return + } + + accessTokenDuration, _ := time.ParseDuration("15m") + accessToken, accessPayload, err := server.tokenMaker.CreateToken( + user.Username, + accessTokenDuration, + ) + if err != nil { + ctx.JSON(http.StatusInternalServerError, errorResponse(err)) + return + } + + refreshTokenDuration, _ := time.ParseDuration("24h") + refreshToken, refreshPayload, err := server.tokenMaker.CreateToken( + user.Username, + refreshTokenDuration, + ) + if err != nil { + ctx.JSON(http.StatusInternalServerError, errorResponse(err)) + return + } + + var createSessionParams model.Session + createSessionParams.Id = refreshPayload.ID + createSessionParams.Username = req.Username + createSessionParams.RefreshToken = refreshToken + createSessionParams.UserAgent = ctx.Request.UserAgent() + createSessionParams.ClientIp = ctx.ClientIP() + createSessionParams.ExpiresAt = refreshPayload.ExpiredAt + + session, err := model.CreateSession(&createSessionParams) + if err != nil { + ctx.JSON(http.StatusInternalServerError, errorResponse(err)) + return + } + + rsp := loginUserResponse{ + SessionID: session.Id, + AccessToken: accessToken, + AccessTokenExpiresAt: accessPayload.ExpiredAt, + RefreshToken: refreshToken, + RefreshTokenExpiresAt: refreshPayload.ExpiredAt, + User: newUserResponse(user), + } + ctx.JSON(http.StatusOK, rsp) +} + +func CheckPassword(password, hasPassword string) bool { + return password == hasPassword } func (server *Server) listUsers(ctx *gin.Context) { @@ -31,70 +139,6 @@ func (server *Server) listUsers(ctx *gin.Context) { ctx.JSON(http.StatusOK, users) } -func (server *Server) createUser(ctx *gin.Context) { - var req createUserRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - ctx.JSON(http.StatusBadRequest, errorResponse(err)) - return - } - - rsp := "" - ctx.JSON(http.StatusOK, rsp) -} - -type loginUserRequest struct { - Username string `json:"username" binding:"required,alphanum"` - Password string `json:"password" binding:"required,min=6"` -} - -type loginUserResponse struct { - SessionID string `json:"session_id"` - AccessToken string `json:"access_token"` - AccessTokenExpiresAt time.Time `json:"access_token_expires_at"` - RefreshToken string `json:"refresh_token"` - RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"` - User userResponse `json:"user"` -} - -type Account struct { - ID int64 `json:"id"` - Owner string `json:"owner"` - Balance int64 `json:"balance"` - Currency string `json:"currency"` -} - -func (server *Server) welcome(ctx *gin.Context) { - ctx.JSON(http.StatusOK, "welcome") -} - -func (server *Server) loginUser(ctx *gin.Context) { - ctx.JSON(http.StatusOK, "loginUser") -} - -func (server *Server) createAccount(ctx *gin.Context) { +func (server *Server) getUser(ctx *gin.Context) { } - -func (server *Server) getAccount(ctx *gin.Context) { - -} - -func (server *Server) listAccounts(ctx *gin.Context) { - account1 := &Account{ - 1, - "2", - 3, - "4", - } - account2 := &Account{ - 11, - "22", - 33, - "44", - } - var accounts []*Account - accounts = append(accounts, account1) - accounts = append(accounts, account2) - - ctx.JSON(http.StatusOK, accounts) -} diff --git a/server/adminserver/db/db.go b/server/adminserver/db/db.go index bcb5a464..979d42a9 100644 --- a/server/adminserver/db/db.go +++ b/server/adminserver/db/db.go @@ -12,7 +12,7 @@ var db *sqlx.DB func InitDB() { var err error - dsn := "root:keji178@tcp(login-test.kingsome.cn:3306)/frienddb_dev_1?charset=utf8mb4&parseTime=True" + dsn := "root:keji178@tcp(login-test.kingsome.cn:3306)/admindb_dev?charset=utf8mb4&parseTime=True" db, err = sqlx.Connect("mysql", dsn) if err != nil { f5.GetSysLog().Info("Failed to connect to the database err:%v \n", err) diff --git a/server/adminserver/go.mod b/server/adminserver/go.mod index e2830f24..3400974f 100644 --- a/server/adminserver/go.mod +++ b/server/adminserver/go.mod @@ -18,6 +18,9 @@ require ( ) require ( + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 // indirect + github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/bytedance/sonic v1.10.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.0 // indirect @@ -28,6 +31,7 @@ require ( github.com/go-playground/validator/v10 v10.15.3 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/gomodule/redigo v1.8.3 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -36,11 +40,13 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/o1egl/paseto v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.5.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/net v0.14.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/server/adminserver/go.sum b/server/adminserver/go.sum index c9c7f6a5..1b698f47 100644 --- a/server/adminserver/go.sum +++ b/server/adminserver/go.sum @@ -1,3 +1,10 @@ +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU= +github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 h1:1DcvRPZOdbQRg5nAHt2jrc5QbV0AGuhDdfQI6gXjiFE= +github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU= +github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= +github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk= @@ -30,6 +37,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/gomodule/redigo v1.8.3 h1:HR0kYDX2RJZvAup8CsiJwxB4dTCSC0AaUq6S4SiLwUc= github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= @@ -55,13 +64,19 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/o1egl/paseto v1.0.0 h1:bwpvPu2au176w4IBlhbyUv/S5VPptERIA99Oap5qUd0= +github.com/o1egl/paseto v1.0.0/go.mod h1:5HxsZPmw/3RI2pAwGo1HhOOwSdvBpcuVzO7uDkm+CLU= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -78,12 +93,16 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= diff --git a/server/adminserver/model/session.go b/server/adminserver/model/session.go new file mode 100644 index 00000000..5a1f3dad --- /dev/null +++ b/server/adminserver/model/session.go @@ -0,0 +1,31 @@ +package model + +import ( + "log" + "main/db" + "time" +) + +type Session struct { + Id int64 `db:"id"` + Username string `db:"username"` + RefreshToken string `db:"refresh_token"` + UserAgent string `db:"user_agent"` + ClientIp string `db:"client_ip"` + ExpiresAt time.Time `db:"expires_at"` +} + +func CreateSession(session *Session) (*Session, error) { + query := "INSERT INTO t_sessions (id, username, refresh_token, client_ip, user_agent, expires_at) VALUES (?, ?, ?, ?, ?, ?)" + result, err := db.GetDB().Exec(query, session.Id, session.Username, session.RefreshToken, session.UserAgent, session.ClientIp, session.ExpiresAt) + if err != nil { + log.Printf("Error creating session: %v", err) + return nil, err + } + _, err = result.LastInsertId() + if err != nil { + log.Printf("Error getting last insert ID: %v", err) + return nil, err + } + return session, nil +} diff --git a/server/adminserver/model/user.go b/server/adminserver/model/user.go index 871c0b0b..5994cdb0 100644 --- a/server/adminserver/model/user.go +++ b/server/adminserver/model/user.go @@ -6,17 +6,16 @@ import ( "main/db" ) -// User 结构体表示用户 type User struct { - AccountId string `db:"account_id"` - Username string `db:"name"` - Level int32 `db:"level"` + Id string `db:"id"` + Username string `db:"username"` + Password string `db:"password"` } // CreateUser 创建新用户 func CreateUser(user *User) (int64, error) { - query := "INSERT INTO users (username, email) VALUES (?, ?)" - result, err := db.GetDB().Exec(query, user.Username, user.Level) + query := "INSERT INTO t_users (username, password) VALUES (?, ?)" + result, err := db.GetDB().Exec(query, user.Username, user.Password) if err != nil { log.Printf("Error creating user: %v", err) return 0, err @@ -29,11 +28,10 @@ func CreateUser(user *User) (int64, error) { return userID, nil } -// GetUserByID 根据用户ID获取用户信息 -func GetUserByID(userID int64) (*User, error) { - query := "SELECT id, username, email FROM users WHERE id = ?" +func GetUserByUsername(username string) (*User, error) { + query := "SELECT id, username, password FROM t_users WHERE username = ?" var user User - err := db.GetDB().Get(&user, query, userID) + err := db.GetDB().Get(&user, query, username) if err != nil { if err == sql.ErrNoRows { return nil, nil // 用户不存在 @@ -46,8 +44,8 @@ func GetUserByID(userID int64) (*User, error) { // UpdateUser 更新用户信息 func UpdateUser(user *User) error { - query := "UPDATE users SET username = ?, level = ? WHERE id = ?" - _, err := db.GetDB().Exec(query, user.Username, user.Level, user.AccountId) + query := "UPDATE users SET username WHERE id = ?" + _, err := db.GetDB().Exec(query, user.Username, user.Id) if err != nil { log.Printf("Error updating user: %v", err) return err @@ -68,7 +66,7 @@ func DeleteUser(userID int64) error { // ListUsers 获取所有用户列表 func ListUsers() ([]User, error) { - query := "SELECT account_id, name, level FROM t_user" + query := "SELECT id, username, password FROM t_users" var users []User err := db.GetDB().Select(&users, query) if err != nil { diff --git a/server/adminserver/token/jwt_maker.go b/server/adminserver/token/jwt_maker.go new file mode 100644 index 00000000..3001f584 --- /dev/null +++ b/server/adminserver/token/jwt_maker.go @@ -0,0 +1,54 @@ +package token + +import ( + "fmt" + "github.com/golang-jwt/jwt/v5" + "time" +) + +const minSecretKeySize = 32 + +type JWTMaker struct { + secretKey string +} + +func NewJWTMaker(secretKey string) (Maker, error) { + if len(secretKey) < minSecretKeySize { + return nil, fmt.Errorf("invalid key size: must be at least %d characters", minSecretKeySize) + } + return &JWTMaker{secretKey}, nil +} + +func (maker *JWTMaker) CreateToken(username string, duration time.Duration) (string, *Payload, error) { + payload, err := NewPayload(username, duration) + if err != nil { + return "", payload, err + } + + jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, payload) + token, err := jwtToken.SignedString([]byte(maker.secretKey)) + return token, payload, err +} + +// VerifyToken checks if the token is valid or not +func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) { + keyFunc := func(token *jwt.Token) (interface{}, error) { + _, ok := token.Method.(*jwt.SigningMethodHMAC) + if !ok { + return nil, ErrInvalidToken + } + return []byte(maker.secretKey), nil + } + + jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc) + if err != nil { + return nil, ErrInvalidToken + } + + payload, ok := jwtToken.Claims.(*Payload) + if !ok { + return nil, ErrInvalidToken + } + + return payload, nil +} diff --git a/server/adminserver/token/paseto_maker.go b/server/adminserver/token/paseto_maker.go new file mode 100644 index 00000000..0f63d5e2 --- /dev/null +++ b/server/adminserver/token/paseto_maker.go @@ -0,0 +1,57 @@ +package token + +import ( + "fmt" + "time" + + "github.com/aead/chacha20poly1305" + "github.com/o1egl/paseto" +) + +// PasetoMaker is a PASETO token maker +type PasetoMaker struct { + paseto *paseto.V2 + symmetricKey []byte +} + +// NewPasetoMaker creates a new PasetoMaker +func NewPasetoMaker(symmetricKey string) (Maker, error) { + if len(symmetricKey) != chacha20poly1305.KeySize { + return nil, fmt.Errorf("invalid key size: must be exactly %d characters", chacha20poly1305.KeySize) + } + + maker := &PasetoMaker{ + paseto: paseto.NewV2(), + symmetricKey: []byte(symmetricKey), + } + + return maker, nil +} + +// CreateToken creates a new token for a specific username and duration +func (maker *PasetoMaker) CreateToken(username string, duration time.Duration) (string, *Payload, error) { + payload, err := NewPayload(username, duration) + if err != nil { + return "", payload, err + } + + token, err := maker.paseto.Encrypt(maker.symmetricKey, payload, nil) + return token, payload, err +} + +// VerifyToken checks if the token is valid or not +func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) { + payload := &Payload{} + + err := maker.paseto.Decrypt(token, maker.symmetricKey, payload, nil) + if err != nil { + return nil, ErrInvalidToken + } + + err = payload.Valid() + if err != nil { + return nil, err + } + + return payload, nil +} diff --git a/server/adminserver/token/payload.go b/server/adminserver/token/payload.go index 72896e0b..46dbea85 100644 --- a/server/adminserver/token/payload.go +++ b/server/adminserver/token/payload.go @@ -3,6 +3,7 @@ package token import ( "errors" "f5" + "github.com/golang-jwt/jwt/v5" "time" ) @@ -18,6 +19,13 @@ type Payload struct { ExpiredAt time.Time `json:"expired_at"` } +//type Payload struct { +// ID int64 `json:"id"` +// Username string `json:"username"` +// IssuedAt int64 `json:"issued_at"` +// ExpiredAt int64 `json:"expired_at"` +//} + func NewPayload(username string, duration time.Duration) (*Payload, error) { tokenID := f5.GetApp().NewUuid() payload := &Payload{ @@ -35,3 +43,33 @@ func (payload *Payload) Valid() error { } return nil } + +func (p *Payload) GetExpirationTime() (*jwt.NumericDate, error) { + expirationTime := jwt.NewNumericDate(p.ExpiredAt) + return expirationTime, nil +} + +func (p *Payload) GetIssuedAt() (*jwt.NumericDate, error) { + issuedAt := jwt.NewNumericDate(p.IssuedAt) + return issuedAt, nil + //return p.IssuedAt +} + +func (p *Payload) GetNotBefore() (*jwt.NumericDate, error) { + now := jwt.NewNumericDate(time.Time{}) + return now, nil + //return time.Time{} // 默认返回零值,或根据需求设置合适的值 +} + +func (p *Payload) GetIssuer() (string, error) { + return "your_issuer", nil // 可根据需求返回合适的签发者(Issuer)信息 +} + +func (p *Payload) GetSubject() (string, error) { + return p.Username, nil // 使用用户名作为主题(Subject) +} + +func (p *Payload) GetAudience() (jwt.ClaimStrings, error) { + audience := "your_audience" // 根据需求设置合适的受众(Audience)信息 + return jwt.ClaimStrings{audience}, nil +}