mirror of
https://gitee.com/shikong-sk/gofiber-study
synced 2025-02-23 15:22:14 +08:00
添加 refreshToken 令牌刷新
common.Time json 序列化 与 反序列化完善
This commit is contained in:
parent
362b1ebc39
commit
40f98a050f
@ -12,8 +12,20 @@ const (
|
||||
)
|
||||
|
||||
// MarshalJSON on Json Time format Time field with %Y-%m-%d %H:%M:%S
|
||||
func (t Time) MarshalJSON() ([]byte, error) {
|
||||
func (t *Time) MarshalJSON() ([]byte, error) {
|
||||
// 重写time转换成json之后的格式
|
||||
var tmp = fmt.Sprintf("\"%s\"", time.Time(t).Format(timeFormat))
|
||||
var tmp = fmt.Sprintf("\"%s\"", time.Time(*t).Format(timeFormat))
|
||||
return []byte(tmp), nil
|
||||
}
|
||||
|
||||
func (t *Time) UnmarshalJSON(data []byte) error {
|
||||
// Ignore null, like in the main JSON package.
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
// Fractional seconds are handled implicitly by Parse.
|
||||
var err error
|
||||
rawT, err := time.Parse(`"`+timeFormat+`"`, string(data))
|
||||
*t = Time(rawT)
|
||||
return err
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func NewController(app *fiber.App) *Controller {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param vo body dto.Login true "用户登录"
|
||||
// @Success 200 {object} response.Response{data=string}
|
||||
// @Success 200 {object} response.Response{data=vo.Login}
|
||||
// @Failure default {object} errorx.CodeErrorResponse
|
||||
// @Router /auth/login [post]
|
||||
func (c *Controller) Login() {
|
||||
@ -42,11 +42,39 @@ func (c *Controller) Login() {
|
||||
return ctx.JSON(err)
|
||||
}
|
||||
|
||||
token, err := auth.Services.Login(login)
|
||||
result, err := auth.Services.Login(login)
|
||||
if err = errorx.ParseError(err); err != nil {
|
||||
return ctx.JSON(err)
|
||||
}
|
||||
|
||||
return ctx.JSON(response.NewResponse(token))
|
||||
return ctx.JSON(response.NewResponse(result))
|
||||
})
|
||||
}
|
||||
|
||||
// RefreshToken 刷新令牌
|
||||
//
|
||||
// @Summary 刷新令牌
|
||||
// @Description 刷新令牌
|
||||
// @Tags Auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param vo body dto.RefreshToken true "刷新令牌"
|
||||
// @Success 200 {object} response.Response{data=vo.Login}
|
||||
// @Failure default {object} errorx.CodeErrorResponse
|
||||
// @Router /auth/refreshToken [post]
|
||||
func (c *Controller) RefreshToken() {
|
||||
c.Router.Post("refreshToken", func(ctx *fiber.Ctx) error {
|
||||
refresh := &dto.RefreshToken{}
|
||||
err := ctx.BodyParser(refresh)
|
||||
if err = errorx.ParseError(err); err != nil {
|
||||
return ctx.JSON(err)
|
||||
}
|
||||
|
||||
result, err := auth.Services.RefreshToken(refresh.RefreshToken)
|
||||
if err = errorx.ParseError(err); err != nil {
|
||||
return ctx.JSON(err)
|
||||
}
|
||||
|
||||
return ctx.JSON(response.NewResponse(result))
|
||||
})
|
||||
}
|
||||
|
78
docs/docs.go
78
docs/docs.go
@ -60,7 +60,59 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/vo.Login"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/errorx.CodeErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/refreshToken": {
|
||||
"post": {
|
||||
"description": "刷新令牌",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "刷新令牌",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "刷新令牌",
|
||||
"name": "vo",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.RefreshToken"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/vo.Login"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -642,6 +694,15 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RefreshToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refreshToken": {
|
||||
"type": "string",
|
||||
"example": "0123456789ABCDEFG"
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorx.CodeErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -742,6 +803,21 @@ const docTemplate = `{
|
||||
"example": "OK"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vo.Login": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refreshToken": {
|
||||
"description": "refreshToken 刷新令牌",
|
||||
"type": "string",
|
||||
"example": "0123456789ABCDEFG"
|
||||
},
|
||||
"token": {
|
||||
"description": "token 用户令牌",
|
||||
"type": "string",
|
||||
"example": "0123456789ABCDEFG"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
@ -52,7 +52,59 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/vo.Login"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/errorx.CodeErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/refreshToken": {
|
||||
"post": {
|
||||
"description": "刷新令牌",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "刷新令牌",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "刷新令牌",
|
||||
"name": "vo",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.RefreshToken"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/vo.Login"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -634,6 +686,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RefreshToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refreshToken": {
|
||||
"type": "string",
|
||||
"example": "0123456789ABCDEFG"
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorx.CodeErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -734,6 +795,21 @@
|
||||
"example": "OK"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vo.Login": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refreshToken": {
|
||||
"description": "refreshToken 刷新令牌",
|
||||
"type": "string",
|
||||
"example": "0123456789ABCDEFG"
|
||||
},
|
||||
"token": {
|
||||
"description": "token 用户令牌",
|
||||
"type": "string",
|
||||
"example": "0123456789ABCDEFG"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
@ -9,6 +9,12 @@ definitions:
|
||||
example: "12341234"
|
||||
type: string
|
||||
type: object
|
||||
dto.RefreshToken:
|
||||
properties:
|
||||
refreshToken:
|
||||
example: 0123456789ABCDEFG
|
||||
type: string
|
||||
type: object
|
||||
errorx.CodeErrorResponse:
|
||||
properties:
|
||||
code:
|
||||
@ -78,6 +84,17 @@ definitions:
|
||||
example: OK
|
||||
type: string
|
||||
type: object
|
||||
vo.Login:
|
||||
properties:
|
||||
refreshToken:
|
||||
description: refreshToken 刷新令牌
|
||||
example: 0123456789ABCDEFG
|
||||
type: string
|
||||
token:
|
||||
description: token 用户令牌
|
||||
example: 0123456789ABCDEFG
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact:
|
||||
email: 919411476@qq.com
|
||||
@ -112,7 +129,7 @@ paths:
|
||||
- $ref: '#/definitions/response.Response'
|
||||
- properties:
|
||||
data:
|
||||
type: string
|
||||
$ref: '#/definitions/vo.Login'
|
||||
type: object
|
||||
default:
|
||||
description: ""
|
||||
@ -121,6 +138,37 @@ paths:
|
||||
summary: 用户登录
|
||||
tags:
|
||||
- Auth
|
||||
/auth/refreshToken:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 刷新令牌
|
||||
parameters:
|
||||
- description: 刷新令牌
|
||||
in: body
|
||||
name: vo
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.RefreshToken'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/vo.Login'
|
||||
type: object
|
||||
default:
|
||||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/errorx.CodeErrorResponse'
|
||||
summary: 刷新令牌
|
||||
tags:
|
||||
- Auth
|
||||
/casbin/getUserRoles:
|
||||
get:
|
||||
consumes:
|
||||
|
@ -1,6 +1,15 @@
|
||||
package dto
|
||||
|
||||
// Login
|
||||
// @Param account body string true "用户账号(account)"
|
||||
// @Param password body string true "用户密码"
|
||||
type Login struct {
|
||||
Account string `json:"account" example:"root"`
|
||||
Password string `json:"password" example:"12341234"`
|
||||
}
|
||||
|
||||
// RefreshToken
|
||||
// @Param refreshToken body string true "刷新令牌"
|
||||
type RefreshToken struct {
|
||||
RefreshToken string `json:"refreshToken" example:"0123456789ABCDEFG"`
|
||||
}
|
||||
|
8
model/vo/auth.go
Normal file
8
model/vo/auth.go
Normal file
@ -0,0 +1,8 @@
|
||||
package vo
|
||||
|
||||
type Login struct {
|
||||
// token 用户令牌
|
||||
Token string `json:"token" example:"0123456789ABCDEFG"`
|
||||
// refreshToken 刷新令牌
|
||||
RefreshToken string `json:"refreshToken" example:"0123456789ABCDEFG"`
|
||||
}
|
@ -1,15 +1,26 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gofiber.study.skcks.cn/common/errorx"
|
||||
"github.com/goccy/go-json"
|
||||
"gofiber.study.skcks.cn/common/logger"
|
||||
"gofiber.study.skcks.cn/common/utils"
|
||||
"gofiber.study.skcks.cn/global"
|
||||
"gofiber.study.skcks.cn/model/dto"
|
||||
"gofiber.study.skcks.cn/model/generic/models"
|
||||
"gofiber.study.skcks.cn/model/vo"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Failed = errors.New("账号或密码错误")
|
||||
InvalidRefreshToken = errors.New("刷新令牌已失效")
|
||||
)
|
||||
|
||||
const (
|
||||
Separator = ":"
|
||||
RefreshTokenPrefix = "RefreshToken" + Separator
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
@ -21,7 +32,19 @@ func InitService() {
|
||||
Services = &Service{}
|
||||
}
|
||||
|
||||
func (s *Service) Login(login *dto.Login) (token string, err error) {
|
||||
func (s *Service) generateAndSaveRefreshToken(user *models.User) (refreshToken string, err error) {
|
||||
refreshToken, err = global.GetNanoId()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
expire := time.Duration(global.Config.Jwt.Expire*2) * time.Second
|
||||
ctx := context.Background()
|
||||
global.Redis.Set(ctx, RefreshTokenPrefix+refreshToken, utils.Json(user), expire)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) Login(login *dto.Login) (result *vo.Login, err error) {
|
||||
user := &models.User{Account: login.Account, Password: login.Password}
|
||||
exist, err := global.DataSources.Get(user)
|
||||
if err != nil {
|
||||
@ -29,14 +52,61 @@ func (s *Service) Login(login *dto.Login) (token string, err error) {
|
||||
}
|
||||
|
||||
if !exist {
|
||||
return token, Failed
|
||||
return nil, Failed
|
||||
}
|
||||
|
||||
token, err = global.GetToken(global.UserClaims{
|
||||
token, err := global.GetToken(global.UserClaims{
|
||||
Id: user.Id,
|
||||
Account: user.Account,
|
||||
})
|
||||
|
||||
err = errorx.ParseError(err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
refreshToken, err := s.generateAndSaveRefreshToken(user)
|
||||
|
||||
return &vo.Login{
|
||||
Token: token,
|
||||
RefreshToken: refreshToken,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (s *Service) RefreshToken(refreshToken string) (result *vo.Login, err error) {
|
||||
ctx := context.Background()
|
||||
data, err := global.Redis.Get(ctx, RefreshTokenPrefix+refreshToken).Result()
|
||||
if err != nil {
|
||||
return nil, InvalidRefreshToken
|
||||
}
|
||||
|
||||
global.Redis.Del(ctx, RefreshTokenPrefix+refreshToken)
|
||||
|
||||
cache := &models.User{}
|
||||
err = json.Unmarshal([]byte(data), cache)
|
||||
if err != nil {
|
||||
return nil, InvalidRefreshToken
|
||||
}
|
||||
|
||||
user := &models.User{Id: cache.Id, Account: cache.Account}
|
||||
exist, err := global.DataSources.Get(user)
|
||||
if !exist {
|
||||
logger.Log.Infof("未能从 %s 找到用户信息", RefreshTokenPrefix+refreshToken)
|
||||
return nil, InvalidRefreshToken
|
||||
}
|
||||
|
||||
token, err := global.GetToken(global.UserClaims{
|
||||
Id: user.Id,
|
||||
Account: user.Account,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
refreshToken, err = s.generateAndSaveRefreshToken(user)
|
||||
|
||||
return &vo.Login{
|
||||
Token: token,
|
||||
RefreshToken: refreshToken,
|
||||
}, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user