diff --git a/app/captcha.go b/app/captcha.go index c2f6470..89c09ab 100644 --- a/app/captcha.go +++ b/app/captcha.go @@ -16,7 +16,7 @@ func reloadCaptcha(c *config.BasicConfig, redis *redis.Client) { NoiseCount: c.Captcha.NoiseCount, Fonts: []string{ - "SourceHanSansCN-VF-2.otf", + "wqy-microhei.ttc", }, ShowLineOptions: c.Captcha.ShowLineOptions, Length: c.Captcha.Length, diff --git a/common/config/config.go b/common/config/config.go index 7ddc330..e0b8d51 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -66,4 +66,6 @@ type CaptchaConfig struct { Source string `yaml:"source"` //BgColor captcha image background color (optional) BgColor *color.RGBA `yaml:"bgColor"` + //Expire 失效时间 (秒) + Expire int64 `json:"expired"` } diff --git a/config.yaml b/config.yaml index 3d22ad7..30ac001 100644 --- a/config.yaml +++ b/config.yaml @@ -40,4 +40,5 @@ captcha: width: 200 noiseCount: 100 showLineOptions: 4 - source: 1234567890qwertyuioplkjhgfdsazxcvbnm \ No newline at end of file + source: 1234567890qwertyuioplkjhgfdsazxcvbnm + expire: 300 \ No newline at end of file diff --git a/controller/test/test.go b/controller/test/test.go index bb5f934..675073c 100644 --- a/controller/test/test.go +++ b/controller/test/test.go @@ -3,9 +3,13 @@ package test import ( "github.com/gofiber/fiber/v2" "gofiber.study.skcks.cn/common/errorx" + "gofiber.study.skcks.cn/common/logger" "gofiber.study.skcks.cn/common/response" "gofiber.study.skcks.cn/controller/types" "gofiber.study.skcks.cn/global" + "gofiber.study.skcks.cn/model/dto" + "gofiber.study.skcks.cn/model/vo" + "time" ) type Controller struct { @@ -120,8 +124,18 @@ func (c *Controller) NanoIdTest() { }) } -func (c *Controller) GetCaptcha() { - c.Router.Add(fiber.MethodGet, "/getCaptcha", func(ctx *fiber.Ctx) error { +// GetCaptchaTest 验证码 生成测试 +// +// @Summary 验证码 生成测试 +// @Description 验证码 生成测试 +// @Tags Test +// @Accept json +// @Produce json +// @Success 200 {object} response.Response{data=vo.Captcha} +// @Failure default {object} errorx.CodeErrorResponse +// @Router /test/captcha/get [get] +func (c *Controller) GetCaptchaTest() { + c.Router.Add(fiber.MethodGet, "/captcha/get", func(ctx *fiber.Ctx) error { id, content, answer := global.Captcha.Driver.GenerateIdQuestionAnswer() item, err := global.Captcha.Driver.DrawCaptcha(content) err = global.Captcha.Store.Set(id, answer) @@ -129,23 +143,36 @@ func (c *Controller) GetCaptcha() { return ctx.JSON(errorx.NewDefaultError("验证码生成失败")) } - type resp struct { - Id string `json:"id"` - Data string `json:"captcha"` - } - return ctx.JSON(response.NewResponse(resp{ - id, - item.EncodeB64string(), + return ctx.JSON(response.NewResponse(vo.Captcha{ + Id: id, + Base64: item.EncodeB64string(), + Expire: time.Now().Add(time.Duration(global.Config.Captcha.Expire) * time.Second).Unix(), })) }) } -func (c *Controller) VerifyCaptcha() { - c.Router.Add(fiber.MethodPost, "/verifyCaptcha", func(ctx *fiber.Ctx) error { - id := ctx.Query("id") - captcha := ctx.Query("captcha") +// VerifyCaptchaTest 验证码 验证测试 +// +// @Summary 验证码 验证测试 +// @Description 验证码 验证测试 +// @Tags Test +// @Accept json +// @Produce json +// @Param vo body dto.VerifyCaptcha true "验证码验证" +// @Success 200 {object} response.Response{data=string} +// @Failure default {object} errorx.CodeErrorResponse +// @Router /test/captcha/verify [post] +func (c *Controller) VerifyCaptchaTest() { + c.Router.Add(fiber.MethodPost, "/captcha/verify", func(ctx *fiber.Ctx) error { + verifyCaptcha := &dto.VerifyCaptcha{} - verify := global.Captcha.Verify(id, captcha, true) + if err := errorx.ParseError(ctx.BodyParser(verifyCaptcha)); err != nil { + return ctx.JSON(err) + } + + logger.Log.Debugf("verifyCaptcha %#v", verifyCaptcha) + + verify := global.Captcha.Store.Verify(verifyCaptcha.Id, verifyCaptcha.Captcha, true) if !verify { return ctx.JSON(errorx.NewDefaultError("验证码错误")) } diff --git a/docs/docs.go b/docs/docs.go index 8725eee..19816e9 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -442,6 +442,99 @@ const docTemplate = `{ } } }, + "/test/captcha/get": { + "get": { + "description": "验证码 生成测试", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "验证码 生成测试", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/vo.Captcha" + } + } + } + ] + } + }, + "default": { + "description": "", + "schema": { + "$ref": "#/definitions/errorx.CodeErrorResponse" + } + } + } + } + }, + "/test/captcha/verify": { + "post": { + "description": "验证码 验证测试", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "验证码 验证测试", + "parameters": [ + { + "description": "验证码验证", + "name": "vo", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.VerifyCaptcha" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + }, + "default": { + "description": "", + "schema": { + "$ref": "#/definitions/errorx.CodeErrorResponse" + } + } + } + } + }, "/test/jwt": { "get": { "description": "jwt token 解析测试", @@ -683,6 +776,10 @@ const docTemplate = `{ "definitions": { "dto.Login": { "type": "object", + "required": [ + "account", + "password" + ], "properties": { "account": { "type": "string", @@ -696,6 +793,9 @@ const docTemplate = `{ }, "dto.RefreshToken": { "type": "object", + "required": [ + "refreshToken" + ], "properties": { "refreshToken": { "type": "string", @@ -703,6 +803,19 @@ const docTemplate = `{ } } }, + "dto.VerifyCaptcha": { + "type": "object", + "properties": { + "captcha": { + "type": "string", + "example": "abcde" + }, + "id": { + "type": "string", + "example": "abcdefg123456" + } + } + }, "errorx.CodeErrorResponse": { "type": "object", "properties": { @@ -749,6 +862,9 @@ const docTemplate = `{ }, "id": { "type": "string" + }, + "identify": { + "type": "string" } } }, @@ -804,6 +920,21 @@ const docTemplate = `{ } } }, + "vo.Captcha": { + "type": "object", + "properties": { + "base64": { + "description": "验证码图片 base64", + "type": "string", + "example": "data:image/png;base64,iVBO..." + }, + "id": { + "description": "验证码 id", + "type": "string", + "example": "abcdefg123456" + } + } + }, "vo.Login": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index b129fa2..1df356a 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -434,6 +434,99 @@ } } }, + "/test/captcha/get": { + "get": { + "description": "验证码 生成测试", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "验证码 生成测试", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/vo.Captcha" + } + } + } + ] + } + }, + "default": { + "description": "", + "schema": { + "$ref": "#/definitions/errorx.CodeErrorResponse" + } + } + } + } + }, + "/test/captcha/verify": { + "post": { + "description": "验证码 验证测试", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "验证码 验证测试", + "parameters": [ + { + "description": "验证码验证", + "name": "vo", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.VerifyCaptcha" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + }, + "default": { + "description": "", + "schema": { + "$ref": "#/definitions/errorx.CodeErrorResponse" + } + } + } + } + }, "/test/jwt": { "get": { "description": "jwt token 解析测试", @@ -675,6 +768,10 @@ "definitions": { "dto.Login": { "type": "object", + "required": [ + "account", + "password" + ], "properties": { "account": { "type": "string", @@ -688,6 +785,9 @@ }, "dto.RefreshToken": { "type": "object", + "required": [ + "refreshToken" + ], "properties": { "refreshToken": { "type": "string", @@ -695,6 +795,19 @@ } } }, + "dto.VerifyCaptcha": { + "type": "object", + "properties": { + "captcha": { + "type": "string", + "example": "abcde" + }, + "id": { + "type": "string", + "example": "abcdefg123456" + } + } + }, "errorx.CodeErrorResponse": { "type": "object", "properties": { @@ -741,6 +854,9 @@ }, "id": { "type": "string" + }, + "identify": { + "type": "string" } } }, @@ -796,6 +912,21 @@ } } }, + "vo.Captcha": { + "type": "object", + "properties": { + "base64": { + "description": "验证码图片 base64", + "type": "string", + "example": "data:image/png;base64,iVBO..." + }, + "id": { + "description": "验证码 id", + "type": "string", + "example": "abcdefg123456" + } + } + }, "vo.Login": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 3704a50..d74cb2e 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -8,12 +8,26 @@ definitions: password: example: "12341234" type: string + required: + - account + - password type: object dto.RefreshToken: properties: refreshToken: example: 0123456789ABCDEFG type: string + required: + - refreshToken + type: object + dto.VerifyCaptcha: + properties: + captcha: + example: abcde + type: string + id: + example: abcdefg123456 + type: string type: object errorx.CodeErrorResponse: properties: @@ -48,6 +62,8 @@ definitions: type: string id: type: string + identify: + type: string type: object models.User: properties: @@ -84,6 +100,17 @@ definitions: example: OK type: string type: object + vo.Captcha: + properties: + base64: + description: 验证码图片 base64 + example: data:image/png;base64,iVBO... + type: string + id: + description: 验证码 id + example: abcdefg123456 + type: string + type: object vo.Login: properties: refreshToken: @@ -366,6 +393,61 @@ paths: summary: 获取所有路由 tags: - Routes + /test/captcha/get: + get: + consumes: + - application/json + description: 验证码 生成测试 + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/vo.Captcha' + type: object + default: + description: "" + schema: + $ref: '#/definitions/errorx.CodeErrorResponse' + summary: 验证码 生成测试 + tags: + - Test + /test/captcha/verify: + post: + consumes: + - application/json + description: 验证码 验证测试 + parameters: + - description: 验证码验证 + in: body + name: vo + required: true + schema: + $ref: '#/definitions/dto.VerifyCaptcha' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + type: string + type: object + default: + description: "" + schema: + $ref: '#/definitions/errorx.CodeErrorResponse' + summary: 验证码 验证测试 + tags: + - Test /test/jwt: get: consumes: diff --git a/fonts/SourceHanSansCN-VF-2.otf b/fonts/SourceHanSansCN-VF-2.otf deleted file mode 100644 index 4bf10f4..0000000 Binary files a/fonts/SourceHanSansCN-VF-2.otf and /dev/null differ diff --git a/global/captcha.go b/global/captcha.go index 974fe3f..bf45ea1 100644 --- a/global/captcha.go +++ b/global/captcha.go @@ -28,7 +28,7 @@ func NewCaptchaStore(redis *redis.Client) *CaptchaStore { } func (c *CaptchaStore) Set(id string, value string) error { - return c.Store.SetEX(c.Ctx, CaptchaPrefix+CaptchaSeparator+id, value, 180*time.Second).Err() + return c.Store.SetEX(c.Ctx, CaptchaPrefix+CaptchaSeparator+id, value, time.Duration(Config.Captcha.Expire)*time.Second).Err() } func (c *CaptchaStore) Get(id string, clear bool) string { @@ -45,6 +45,6 @@ func (c *CaptchaStore) Get(id string, clear bool) string { } func (c *CaptchaStore) Verify(id, answer string, clear bool) (match bool) { - match = strings.EqualFold(c.Get(id, clear), answer) + match = strings.EqualFold(c.Get(id, clear), strings.TrimSpace(answer)) return } diff --git a/model/dto/captcha.go b/model/dto/captcha.go new file mode 100644 index 0000000..b14b399 --- /dev/null +++ b/model/dto/captcha.go @@ -0,0 +1,9 @@ +package dto + +// VerifyCaptcha +// @Param id body string true "验证码 id" +// @Param captcha body string true "验证码" +type VerifyCaptcha struct { + Id string `json:"id" example:"abcdefg123456"` + Captcha string `json:"captcha" example:"abcde"` +} diff --git a/model/vo/captcha.go b/model/vo/captcha.go new file mode 100644 index 0000000..6653f06 --- /dev/null +++ b/model/vo/captcha.go @@ -0,0 +1,10 @@ +package vo + +type Captcha struct { + // 验证码 id + Id string `json:"id" example:"abcdefg123456"` + // 验证码图片 base64 + Base64 string `json:"base64" example:"data:image/png;base64,iVBO..."` + // 过期时间 unix + Expire int64 `json:"expire" example:"10000000"` +}