From 2d43931fc8aab6be9d953f3366ea0bdc3e3720a0 Mon Sep 17 00:00:00 2001 From: fallen-angle <1853633282@qq.com> Date: Thu, 10 Feb 2022 17:08:42 +0800 Subject: [PATCH] feat: add jwt --- config/config.go | 7 ++++ handler/article.go | 17 ++++++++++ initialize/mysql.go | 3 +- middleware/gin_recovery.go | 21 ++++++++++-- models/article.go | 16 +++++++++ router/article.go | 13 ++++++++ router/router.go | 1 + service/article/article.go | 17 ++++++++++ settings-dev.yml | 9 ++++-- utils/jwt.go | 66 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 handler/article.go create mode 100644 models/article.go create mode 100644 router/article.go create mode 100644 service/article/article.go create mode 100644 utils/jwt.go diff --git a/config/config.go b/config/config.go index 4190f8c..aaca350 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ type ServerConfig struct { LogPath string `yaml:"logPath"` MySQL MySQLConfig `yaml:"mysql"` Redis RedisConfig `yaml:"redis"` + Jwt JwtConfig `yaml:"jwt"` } type MySQLConfig struct { @@ -24,3 +25,9 @@ type RedisConfig struct { Port int `yaml:"port"` Password string `yaml:"password"` } + +type JwtConfig struct { + Secret string `yaml:"secret"` + RenewExpireDays uint `yaml:"renewExpireDays"` + RenewAheadDays uint `yaml:"renewAheadDays"` +} diff --git a/handler/article.go b/handler/article.go new file mode 100644 index 0000000..681be7b --- /dev/null +++ b/handler/article.go @@ -0,0 +1,17 @@ +package handler + +import ( + "fmt" + "nCovTrack-Backend/models" + "nCovTrack-Backend/utils" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v4" +) + +func SaveArticleHandler(c *gin.Context) { + var articleSave models.Article + c.ShouldBindJSON(&articleSave) + fmt.Println(utils.RenewToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDMxMDA5MDAsImlhdCI6MTY0MzQyNDkwMH0.L8qNmbHJtV8fiKKxGbkZk3DrKBPdvhie_oFooH5hGOY")) + utils.Succ(c, map[string]string{"string": utils.GenerateToken(jwt.MapClaims{})}) +} diff --git a/initialize/mysql.go b/initialize/mysql.go index 1698f8a..54bfb92 100644 --- a/initialize/mysql.go +++ b/initialize/mysql.go @@ -6,12 +6,13 @@ import ( "gorm.io/driver/mysql" "gorm.io/gorm" + "gorm.io/gorm/schema" ) func initMySQL() { mysqlConf := global.ServerSettings.MySQL dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", mysqlConf.Username, mysqlConf.Password, mysqlConf.Host, mysqlConf.Port, mysqlConf.Database) - db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}}) if err != nil { panic(err) } diff --git a/middleware/gin_recovery.go b/middleware/gin_recovery.go index 440b377..af488b4 100644 --- a/middleware/gin_recovery.go +++ b/middleware/gin_recovery.go @@ -49,10 +49,27 @@ func GinRecovery(stack bool) gin.HandlerFunc { } utils.RequestLogError(logParams...) c.AbortWithStatus(http.StatusInternalServerError) - fmt.Println(err) - fmt.Printf("\n%s\n", debug.Stack()) + fmt.Printf("\n%s\n", err) + fmt.Printf("\n%s\n", cutStack(debug.Stack())) } }() c.Next() } } + +func cutStack(stack []byte) string { + stackStr := string(stack) + line := 0 + lastLineCharIndex := 0 + for index, char := range stackStr { + if char == '\n' { + line++ + } + if line == 7 { + lastLineCharIndex = index + 1 + break + } + } + fmt.Println(stackStr[lastLineCharIndex:]) + return "" +} diff --git a/models/article.go b/models/article.go new file mode 100644 index 0000000..1151d92 --- /dev/null +++ b/models/article.go @@ -0,0 +1,16 @@ +package models + +import "time" + +type Article struct { + ID int `gorm:"primaryKey;column:id" json:"-"` // 文章id + CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 文章新建时间 + CreateUser string `gorm:"column:create_user" json:"createUser"` // 文章创建者id + ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 文章最后更新时间 + ModifyUser string `gorm:"column:modify_user" json:"modifyUser"` // 文章最后更新者id + Title string `gorm:"column:title" json:"title"` // 文章标题 + Tags string `gorm:"column:tags" json:"tags"` // 文章Tag + Resume string `gorm:"column:resume" json:"resume"` // 文章简述 + Cover string `gorm:"column:cover" json:"cover"` // 文章封面 + Content string `gorm:"column:content" json:"content"` // 文章内容(如有需要可迁移至对象存储) +} diff --git a/router/article.go b/router/article.go new file mode 100644 index 0000000..f0dcc8d --- /dev/null +++ b/router/article.go @@ -0,0 +1,13 @@ +package router + +import ( + "nCovTrack-Backend/global" + "nCovTrack-Backend/handler" +) + +func articleRouter() { + articleRouter := global.RootRouter.Group("/article") + { + articleRouter.POST("/save", handler.SaveArticleHandler) + } +} diff --git a/router/router.go b/router/router.go index f151bdf..b5b6c1a 100644 --- a/router/router.go +++ b/router/router.go @@ -22,4 +22,5 @@ func BusiRouter() { //}) } statisticRouter() + articleRouter() } diff --git a/service/article/article.go b/service/article/article.go new file mode 100644 index 0000000..166925c --- /dev/null +++ b/service/article/article.go @@ -0,0 +1,17 @@ +package article + +import ( + "nCovTrack-Backend/global" + "nCovTrack-Backend/models" +) + +func GetArticleList() []models.Article { + return make([]models.Article, 0) +} + +func SaveArticle(article models.Article) { + // 前端校验 + // articlea := &models.Article{Content: "#Ceshi", Cover: "https://www.baidu.com/link?url=AWfrkr2rXUGVmKuD08cYx7GwAfQw7qXy_ZczQuH9N_raTP0_eRTv4eZgsdYhtMhS8F7nVl9WfdF01byCD5DAKK&wd=&eqid=b50bb6b100004750000000056202107a", Resume: "sss", Tags: "s", Title: "title", CreateUser: "1", ModifyUser: "1", CreateTime: time.Now(), ModifyTime: time.Now()} + tx := global.Db.Create(article) + print(tx) +} diff --git a/settings-dev.yml b/settings-dev.yml index 5a26d94..ecedebf 100644 --- a/settings-dev.yml +++ b/settings-dev.yml @@ -9,10 +9,15 @@ mysql: host: myhost.fallen-angle.com port: 3306 username: root - password: 13291004986@lm - database: nCovTrack + password: 13291004986 + database: ncov_track redis: host: myhost.fallen-angle.com port: 6379 password: wzl20001001 + +jwt: + secret: bWF5YmVJYWxzb3NhbWV0b2JlZm9yZe + renewExpireDays: 7 + renewAheadDays: 3 diff --git a/utils/jwt.go b/utils/jwt.go new file mode 100644 index 0000000..fd5db7e --- /dev/null +++ b/utils/jwt.go @@ -0,0 +1,66 @@ +package utils + +import ( + "fmt" + "nCovTrack-Backend/global" + "time" + + "github.com/golang-jwt/jwt/v4" +) + +var JWT_KEY = []byte(global.ServerSettings.Jwt.Secret) + +// Generate token for user +// Return: token generated +func GenerateToken(claims jwt.MapClaims) string { + claims["exp"] = time.Now().Add(15 * 24 * time.Hour).Unix() + claims["iat"] = time.Now().Unix() + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenStr, err := token.SignedString(JWT_KEY) + if err != nil { + panic(err) + } + return tokenStr +} + +// Renew user's token +// tokenStr: user request token +// Return: +// BlankString: token is invalid or token is expired out of allowed time; +// OldToken: token is not need to renew; +// NewToekn: token is renew; +func RenewToken(tokenStr string) string { + token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) { + return JWT_KEY, nil + }) + + // Token is invalid + switch err.(*jwt.ValidationError).Errors { + case jwt.ValidationErrorSignatureInvalid: + return "" + case jwt.ValidationErrorIssuedAt: + return "" + } + + claims := token.Claims.(jwt.MapClaims) + expireAt := time.Unix(int64(claims["exp"].(float64)), 0) + expireDuration := expireAt.Sub(time.Now()) + + // Token is out of allow expire duration + if expireDuration.Hours() < float64(-global.ServerSettings.Jwt.RenewExpireDays*24) { + return "" + } + // Token not need renew + if expireDuration.Hours() > float64(global.ServerSettings.Jwt.RenewAheadDays*24) { + return tokenStr + } + fmt.Println(expireDuration) + + claims["exp"] = time.Now().Add(15 * 24 * time.Hour).Unix() + token = jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenStr, err = token.SignedString(JWT_KEY) + if err != nil { + panic(err) + } + return tokenStr +}