jwt
This commit is contained in:
parent
882149eee5
commit
6459a79c32
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -12,9 +12,9 @@
|
|||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
.idea
|
||||
storage/*
|
||||
blog.iml
|
||||
go.sum
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
storage/logs/*
|
||||
storage/uploads/*
|
|
@ -26,4 +26,8 @@ Database:
|
|||
Charset: utf8mb4
|
||||
ParseTime: True
|
||||
MaxIdleConns: 10
|
||||
MaxOpenConns: 30
|
||||
MaxOpenConns: 30
|
||||
JWT:
|
||||
Secret: eddycjy
|
||||
Issuer: blog-service
|
||||
Expire: 7200
|
66
docs/docs.go
66
docs/docs.go
|
@ -32,6 +32,13 @@ var doc = `{
|
|||
],
|
||||
"summary": "获取多个标签",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "token",
|
||||
"name": "token",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"maxLength": 100,
|
||||
"type": "string",
|
||||
|
@ -246,6 +253,62 @@ var doc = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/auth": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "获取token",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "appkey",
|
||||
"name": "app_key",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "appsecret",
|
||||
"name": "app_secret",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/api.res"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/errorcode.Error"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/errorcode.Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/upload/file": {
|
||||
"post": {
|
||||
"produces": [
|
||||
|
@ -297,6 +360,9 @@ var doc = `{
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.res": {
|
||||
"type": "object"
|
||||
},
|
||||
"app.Pager": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -15,6 +15,13 @@
|
|||
],
|
||||
"summary": "获取多个标签",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "token",
|
||||
"name": "token",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"maxLength": 100,
|
||||
"type": "string",
|
||||
|
@ -229,6 +236,62 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/auth": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "获取token",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "appkey",
|
||||
"name": "app_key",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "appsecret",
|
||||
"name": "app_secret",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/api.res"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/errorcode.Error"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/errorcode.Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/upload/file": {
|
||||
"post": {
|
||||
"produces": [
|
||||
|
@ -280,6 +343,9 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.res": {
|
||||
"type": "object"
|
||||
},
|
||||
"app.Pager": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
definitions:
|
||||
api.res:
|
||||
type: object
|
||||
app.Pager:
|
||||
properties:
|
||||
page:
|
||||
|
@ -57,6 +59,11 @@ paths:
|
|||
/api/v1/tags:
|
||||
get:
|
||||
parameters:
|
||||
- description: token
|
||||
in: query
|
||||
name: token
|
||||
required: true
|
||||
type: string
|
||||
- description: 标签名称
|
||||
in: query
|
||||
maxLength: 100
|
||||
|
@ -205,6 +212,40 @@ paths:
|
|||
schema:
|
||||
$ref: '#/definitions/errorcode.Error'
|
||||
summary: 更新标签
|
||||
/auth:
|
||||
post:
|
||||
parameters:
|
||||
- description: appkey
|
||||
in: formData
|
||||
name: app_key
|
||||
required: true
|
||||
type: string
|
||||
- description: appsecret
|
||||
in: formData
|
||||
name: app_secret
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 成功
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/api.res'
|
||||
- properties:
|
||||
token:
|
||||
type: string
|
||||
type: object
|
||||
"400":
|
||||
description: 请求错误
|
||||
schema:
|
||||
$ref: '#/definitions/errorcode.Error'
|
||||
"500":
|
||||
description: 内部错误
|
||||
schema:
|
||||
$ref: '#/definitions/errorcode.Error'
|
||||
summary: 获取token
|
||||
/upload/file:
|
||||
post:
|
||||
parameters:
|
||||
|
|
|
@ -10,4 +10,5 @@ var (
|
|||
AppSetting *setting.AppSettingS
|
||||
DatabaseSetting *setting.DatabaseSettingS
|
||||
Logger *logger.Logger
|
||||
JWTSetting *setting.JWT
|
||||
)
|
||||
|
|
1
go.mod
1
go.mod
|
@ -5,6 +5,7 @@ go 1.16
|
|||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/gin-gonic/gin v1.7.2
|
||||
github.com/go-openapi/spec v0.20.3 // indirect
|
||||
|
|
8
internal/dao/auth.go
Normal file
8
internal/dao/auth.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package dao
|
||||
|
||||
import "blog/internal/model"
|
||||
|
||||
func (d *Dao) GetAuth(appKey, appSecret string) (model.Auth, error) {
|
||||
auth := model.Auth{AppKey: appKey, AppSecret: appSecret}
|
||||
return auth.Get(d.engine)
|
||||
}
|
44
internal/middleware/jwt.go
Normal file
44
internal/middleware/jwt.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"blog/pkg/app"
|
||||
"blog/pkg/errorcode"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func JWT() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var (
|
||||
token string
|
||||
ecode = errorcode.Success
|
||||
)
|
||||
if s, exist := c.GetQuery("token"); exist {
|
||||
token = s
|
||||
} else {
|
||||
token = c.GetHeader("token")
|
||||
}
|
||||
if token == "" {
|
||||
ecode = errorcode.InvalidParams
|
||||
} else {
|
||||
_, err := app.ParseToken(token)
|
||||
if err != nil {
|
||||
switch err.(*jwt.ValidationError).Errors {
|
||||
case jwt.ValidationErrorExpired:
|
||||
ecode = errorcode.UnauthorizedTokenTimeout
|
||||
default:
|
||||
ecode = errorcode.UnauthorizedTokenError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ecode != errorcode.Success {
|
||||
response := app.NewResponse(c)
|
||||
response.ToErrorResponse(ecode)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
24
internal/model/auth.go
Normal file
24
internal/model/auth.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package model
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
type Auth struct {
|
||||
*Model
|
||||
AppKey string `json:"app_key"`
|
||||
AppSecret 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.AppSecret, 0)
|
||||
err := db.First(&auth).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return auth, err
|
||||
}
|
||||
|
||||
return auth, nil
|
||||
}
|
51
internal/routess/api/auth.go
Normal file
51
internal/routess/api/auth.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"blog/global"
|
||||
"blog/internal/service"
|
||||
"blog/pkg/app"
|
||||
"blog/pkg/errorcode"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type res struct {
|
||||
token string
|
||||
}
|
||||
|
||||
// @Summary 获取token
|
||||
// @Produce json
|
||||
// @Param app_key formData string true "appkey" required
|
||||
// @Param app_secret formData string true "appsecret" required
|
||||
// @Success 200 {object} res{token=string} "成功"
|
||||
// @Failure 400 {object} errorcode.Error "请求错误"
|
||||
// @Failure 500 {object} errorcode.Error "内部错误"
|
||||
// @Router /auth [post]
|
||||
func GetAuth(c *gin.Context) {
|
||||
param := service.AuthRequest{}
|
||||
response := app.NewResponse(c)
|
||||
valid, errs := app.BindAndValid(c, ¶m)
|
||||
if !valid {
|
||||
global.Logger.Errorf("app.BindAndValid errs: %v", errs)
|
||||
response.ToErrorResponse(errorcode.InvalidParams.WithDetails(errs.Errors()...))
|
||||
return
|
||||
}
|
||||
|
||||
svc := service.New(c.Request.Context())
|
||||
err := svc.CheckAuth(¶m)
|
||||
if err != nil {
|
||||
global.Logger.Errorf("svc.CheckAuth err: %v", err)
|
||||
response.ToErrorResponse(errorcode.UnauthorizedAuthNotExist)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := app.GenerateToken(param.AppKey, param.AppSecret)
|
||||
if err != nil {
|
||||
global.Logger.Errorf("app.GenerateToken err: %v", err)
|
||||
response.ToErrorResponse(errorcode.UnauthorizedTokenGenerate)
|
||||
return
|
||||
}
|
||||
|
||||
response.ToResponse(gin.H{
|
||||
"token": token,
|
||||
})
|
||||
}
|
|
@ -20,6 +20,7 @@ func (t Tag) Get(c *gin.Context) {}
|
|||
|
||||
// @Summary 获取多个标签
|
||||
// @Produce json
|
||||
// @Param token query string true "token"
|
||||
// @Param name query string false "标签名称" maxlength(100)
|
||||
// @Param state query int false "状态" Enums(0, 1) default(1)
|
||||
// @Param page query int false "页码" default(1)
|
||||
|
|
|
@ -17,6 +17,7 @@ func NewRouter() *gin.Engine {
|
|||
r.Use(gin.Logger())
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(middleware.Translations())
|
||||
|
||||
article := v1.NewArticle()
|
||||
tag := v1.NewTag()
|
||||
upload := api.NewUpload()
|
||||
|
@ -24,7 +25,9 @@ func NewRouter() *gin.Engine {
|
|||
r.StaticFS("/static", http.Dir(global.AppSetting.UploadSavePath))
|
||||
//r.GET("/static/*any",api.ReadFile)
|
||||
r.GET("/doc/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
r.POST("/auth", api.GetAuth)
|
||||
apiv1 := r.Group("/api/v1")
|
||||
apiv1.Use(middleware.JWT())
|
||||
{
|
||||
apiv1.POST("/tags", tag.Create)
|
||||
apiv1.DELETE("/tags/:id", tag.Delete)
|
||||
|
|
21
internal/service/auth.go
Normal file
21
internal/service/auth.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package service
|
||||
|
||||
import "errors"
|
||||
|
||||
type AuthRequest struct {
|
||||
AppKey string `form:"app_key" binding:"required"`
|
||||
AppSecret string `form:"app_secret" binding:"required"`
|
||||
}
|
||||
|
||||
func (svc *Service) CheckAuth(param *AuthRequest) error {
|
||||
auth, err := svc.dao.GetAuth(param.AppKey, param.AppSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if auth.ID > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("auth info does not exist")
|
||||
}
|
6
main.go
6
main.go
|
@ -64,7 +64,12 @@ func setupSetting() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = newSetting.ReadSection("JWT", &global.JWTSetting)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
global.JWTSetting.Expire *= time.Second
|
||||
global.ServerSetting.ReadTimeout *= time.Second
|
||||
global.ServerSetting.WriteTimeout *= time.Second
|
||||
return nil
|
||||
|
@ -76,7 +81,6 @@ func setupSetting() error {
|
|||
// @termsOfService https://github.com/go-programming-tour-book
|
||||
func main() {
|
||||
gin.SetMode(global.ServerSetting.RunMode)
|
||||
global.Logger.Infof("%s: go-programming-tour-book/%s", "eddycjy", "blog-service")
|
||||
|
||||
r := routess.NewRouter()
|
||||
s := &http.Server{
|
||||
|
|
51
pkg/app/jwt.go
Normal file
51
pkg/app/jwt.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"blog/global"
|
||||
"blog/pkg/util"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Claims struct {
|
||||
AppKey string `json:"app_key"`
|
||||
AppSecret string `json:"app_secret"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func GetJWTSecret() []byte {
|
||||
return []byte(global.JWTSetting.Secret)
|
||||
}
|
||||
|
||||
func ParseToken(token string) (*Claims, error) {
|
||||
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return GetJWTSecret(), nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenClaims != nil {
|
||||
if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func GenerateToken(appKey, appSecret string) (string, error) {
|
||||
nowTime := time.Now()
|
||||
expireTime := nowTime.Add(global.JWTSetting.Expire)
|
||||
claims := Claims{
|
||||
AppKey: util.EncodeMD5(appKey),
|
||||
AppSecret: util.EncodeMD5(appSecret),
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: expireTime.Unix(),
|
||||
Issuer: global.JWTSetting.Issuer,
|
||||
},
|
||||
}
|
||||
|
||||
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
token, err := tokenClaims.SignedString(GetJWTSecret())
|
||||
return token, err
|
||||
}
|
|
@ -9,6 +9,12 @@ type ServerSettingS struct {
|
|||
WriteTimeout time.Duration
|
||||
}
|
||||
|
||||
type JWT struct {
|
||||
Secret string
|
||||
Issuer string
|
||||
Expire time.Duration
|
||||
}
|
||||
|
||||
type AppSettingS struct {
|
||||
DefaultPageSize int
|
||||
MaxPageSize int
|
||||
|
|
Loading…
Reference in New Issue
Block a user