Skip to content

Commit

Permalink
<feat>: Add JWT for authorized token
Browse files Browse the repository at this point in the history
- Add auth table to save JWT secret
- Add JWT authorization check in middleware for api

#7
  • Loading branch information
lc-1010 committed Jun 28, 2023
1 parent 236e7de commit 2bc489c
Show file tree
Hide file tree
Showing 19 changed files with 242 additions and 9 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;
```sql
`created_on` int(10) unsigned DEFAULT '0' COMMENT '创建时间',
`created_by` varchar(100) DEFAULT '' COMMENT '创建人',
`modified_on` int(10) unsigned DEFAULT '0' COMMNET '修改时间',
`modified_on` int(10) unsigned DEFAULT '0' COMMENT '修改时间',
`modified_by` varchar(100) DEFAULT '' COMMENT '修改人',
`deleted_on` int(10) unsigned DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint(3) unsigned DEFAULT '0' COMMENT '是否删除 0 未删 , 1 已删',
Expand Down Expand Up @@ -61,7 +61,21 @@ CREATE TABLE `blog_article` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章关联标签';
```

- blog_auth
```sql
CREATE TABLE `blog_auth` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`app_key` varchar(20) DEFAULT '' COMMENT 'key',
`app_secret` varchar(50) DEFAULT '' COMMENT 'secret',
`created_on` int(10) unsigned DEFAULT '0' COMMENT '创建时间',
`created_by` varchar(100) DEFAULT '' COMMENT '创建人',
`modified_on` int(10) unsigned DEFAULT '0' COMMENT '修改时间',
`modified_by` varchar(100) DEFAULT '' COMMENT '修改人',
`deleted_on` int(10) unsigned DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint(3) unsigned DEFAULT '0' COMMENT '是否删除 0 未删 , 1 已删',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT="认证管理"
```

## 建立model

Expand Down
5 changes: 4 additions & 1 deletion configs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ Database:
ParseTime: True
MaxIdleConns: 10
MaxOpenConns: 30

JWT:
Secret: 2301a11ba8c311876c058340e7521995
Issuer: blog-service
Expire: 7200
1 change: 1 addition & 0 deletions global/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ var (
AppSetting *setting.AppSettingS
DatabaseSetting *setting.DatabaseSettingS
Logger *logger.Logger
JWTSetting *setting.JWTSettingS
)
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/lc-1010/OneBlogService
go 1.20

require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.9.1
github.com/go-playground/assert/v2 v2.2.0
github.com/go-playground/locales v0.14.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
Expand Down
8 changes: 8 additions & 0 deletions internal/dao/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dao

import "github.com/lc-1010/OneBlogService/internal/model"

func (d *Dao) GetAuth(appkey, appSercet string) (model.Auth, error) {
auth := model.Auth{AppKey: appkey, AppSecert: appSercet}
return auth.Get(d.engine)
}
46 changes: 46 additions & 0 deletions internal/middleware/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package middleware

import (
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"github.com/lc-1010/OneBlogService/pkg/app"
"github.com/lc-1010/OneBlogService/pkg/errcode"
)

func JWT() gin.HandlerFunc {
return func(ctx *gin.Context) {
var (
token string
ecode = errcode.Success
)

if s, exist := ctx.GetQuery("token"); exist {
token = s
} else {
token = ctx.GetHeader("token")
}

if token == "" {
ecode = errcode.InvalidParams
} else {
_, err := app.ParseToken(token)
if err != nil {
switch err.(*jwt.ValidationError).Errors {
case jwt.ValidationErrorExpired:
ecode = errcode.UnauthoerizedTokenTimeout
default:
ecode = errcode.UnauthoerizedTokenError
}

}
}
if ecode != errcode.Success {
response := app.NewResponse(ctx)
response.ToErrorResponse(ecode)
ctx.Abort()
return
}

ctx.Next()
}
}
31 changes: 31 additions & 0 deletions internal/model/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package model

import (
"errors"

"gorm.io/gorm"
)

type Auth struct {
*Model
AppKey string `json:"app_key"`
AppSecert string `json:"app_secret"`
}

func (a Auth) TableName() string {
return "blog_auth"
}

func (a Auth) Get(db *gorm.DB) (Auth, error) {
var auth Auth
db = db.Where("app_key = ? AND app_secret = ? AND is_del = ?", a.AppKey, a.AppSecert, 0)
err := db.First(&auth).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return auth, nil
} else {
return auth, err
}
}
return auth, nil
}
4 changes: 2 additions & 2 deletions internal/model/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (t BlogTag) Create(db *gorm.DB) error {
func (t BlogTag) CheckName(db *gorm.DB) (BlogTag, error) {
var tag BlogTag
err := db.Where("name = ?", t.Name).First(&tag).Error
if err != nil {
if err != nil && err != gorm.ErrRecordNotFound {
return tag, err
}
return tag, nil
Expand All @@ -93,7 +93,7 @@ func (t BlogTag) Get(db *gorm.DB) (BlogTag, error) {
var tag BlogTag
err := db.Where("id = ? and is_del = ? and state = ?",
t.ID, t.IsDel, t.State).First(&tag).Error
if err != nil {
if err != nil && err != gorm.ErrRecordNotFound {
return tag, err
}
return tag, nil
Expand Down
38 changes: 38 additions & 0 deletions internal/routers/api/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package api

import (
"github.com/gin-gonic/gin"
"github.com/lc-1010/OneBlogService/global"
"github.com/lc-1010/OneBlogService/internal/service"
"github.com/lc-1010/OneBlogService/pkg/app"
"github.com/lc-1010/OneBlogService/pkg/errcode"
)

func GetAuth(c *gin.Context) {
param := service.AuthRequest{}

response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.Logger.Errorf(c, "app.BindAndValid errs :%v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}

svc := service.New(c.Request.Context())
err := svc.CheckAuth(&param)
if err != nil {
global.Logger.Errorf(c, "svc.CheckAuth errs :%v", errs)
response.ToErrorResponse(errcode.UnauthorizedAuthNotExist)
return
}
token, err := app.GenerateToken(param.Appkey, param.AppSercet)
if err != nil {
global.Logger.Errorf(c, "svc.GenerateToken errs :%v", errs)
response.ToErrorResponse(errcode.UnauthoerizedTokenGenerate)
return
}
response.ToResponse(gin.H{
"token": token,
})
}
2 changes: 1 addition & 1 deletion internal/routers/api/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func NewUpload() Upload {
}

func (u Upload) UploadFile(c *gin.Context) {
param := service.UploadParams{
param := service.UploadRequest{
FormName: "file",
FormFileType: "type",
}
Expand Down
5 changes: 5 additions & 0 deletions internal/routers/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func NewRouter() *gin.Engine {
upload := api.NewUpload()
r.POST("/upload/file", upload.UploadFile)
r.StaticFS("/static", http.Dir(global.AppSetting.UploadSavePath))

// auth
r.POST("/auth", api.GetAuth)

//ping test
p := r.Group("/test")
{
Expand All @@ -56,6 +60,7 @@ func NewRouter() *gin.Engine {

// api router
apiv1 := r.Group("/api/v1")
apiv1.Use(middleware.JWT())
{
//tags
apiv1.POST("/tags", tags.Create)
Expand Down
19 changes: 19 additions & 0 deletions internal/service/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package service

import "errors"

type AuthRequest struct {
Appkey string `form:"app_key" binding:"required"`
AppSercet string `form:"app_sercet" binding:"required"`
}

func (svc *Service) CheckAuth(param *AuthRequest) error {
auth, err := svc.dao.GetAuth(param.Appkey, param.AppSercet)
if err != nil {
return err
}
if auth.ID > 0 {
return nil
}
return errors.New("auth info does not exist")
}
2 changes: 2 additions & 0 deletions internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/lc-1010/OneBlogService/internal/dao"
)

// Service
// 使用上下文和dao 的db engien 来处理具体的逻辑
type Service struct {
ctx context.Context
dao *dao.Dao
Expand Down
2 changes: 1 addition & 1 deletion internal/service/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type FileInfo struct {
AccessUrl string
}

type UploadParams struct {
type UploadRequest struct {
FormName string `json:"file,omitempty"`
FormFileType string `json:"type,omitempty"`
}
Expand Down
7 changes: 7 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ func setupSetting() error {
if err != nil {
return err
}
err = setting.ReadSection("JWT", &global.JWTSetting)
if err != nil {
return err
}
//jwt expire 7200 second
global.JWTSetting.Expire *= time.Second

global.ServerSetting.ReadTimeout *= time.Second
global.ServerSetting.WriteTimeout *= time.Second
return nil
Expand Down
4 changes: 4 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@ func TestMultiHandler(t *testing.T) {
assert.Equal(t, w.Body.String(), exp)

}

func TestMain(t *testing.T) {
main()
}
52 changes: 52 additions & 0 deletions pkg/app/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package app

import (
"time"

"github.com/dgrijalva/jwt-go"
"github.com/lc-1010/OneBlogService/global"
"github.com/lc-1010/OneBlogService/pkg/util"
)

type Claims struct {
AppKey string `json:"app_key"`
AppSecert string `json:"app_secert"`
jwt.StandardClaims
}

func GetJWTSecret() []byte {
return []byte(global.JWTSetting.Secret)
}

func GenerateToken(appKey, appSercet string) (string, error) {
nowTime := time.Now()
expireTime := nowTime.Add(global.JWTSetting.Expire)
claims := Claims{
AppKey: util.EncodeMD5(appKey),
AppSecert: util.EncodeMD5(appSercet),
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireTime.Unix(),
Issuer: global.JWTSetting.Issuer,
},
}
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err := tokenClaims.SignedString(GetJWTSecret())
return token, err
}

func ParseToken(token string) (*Claims, error) {
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (any, error) {
return GetJWTSecret(), nil
})
if err != nil {
return nil, err
}

if tokenClaims != nil {
Claims, ok := tokenClaims.Claims.(*Claims)
if ok && tokenClaims.Valid {
return Claims, nil
}
}
return nil, err
}
4 changes: 2 additions & 2 deletions pkg/errcode/common_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (
ServerError = NewError(1000000, "Server Error")
InvalidParams = NewError(1000001, "Invalid Params")
NotFound = NewError(1000002, "Not Found")
UnauthoerizedTokeExists = NewError(1000003, "UnauthoerizedTokeExists")
UnauthorizedAuthNotExist = NewError(1000003, "UnauthoerizedTokeExists")
UnauthoerizedTokenError = NewError(1000004, "Unauthoerized Token Error")
UnauthoerizedTokenTimeout = NewError(1000005, "Unauthoerized Token Timeout")
UnauthoerizedTokenGenerate = NewError(1000006, "Unauthoerized Token Generate")
Expand Down Expand Up @@ -73,7 +73,7 @@ func (e *Error) StatusCode() int {
return http.StatusInternalServerError
case InvalidParams.Code():
return http.StatusBadRequest
case UnauthoerizedTokeExists.Code():
case UnauthorizedAuthNotExist.Code():
fallthrough
case UnauthoerizedTokenError.Code():
fallthrough
Expand Down

0 comments on commit 2bc489c

Please sign in to comment.