✨ add jwt / complete the rotation verification
This commit is contained in:
@@ -3,12 +3,14 @@ package api
|
||||
import (
|
||||
"schisandra-cloud-album/api/auth_api"
|
||||
"schisandra-cloud-album/api/captcha_api"
|
||||
"schisandra-cloud-album/api/sms_api"
|
||||
)
|
||||
|
||||
// Apis 统一导出的api
|
||||
type Apis struct {
|
||||
AuthApi auth_api.AuthAPI
|
||||
CaptchaAPI captcha_api.CaptchaAPI
|
||||
CaptchaApi captcha_api.CaptchaAPI
|
||||
SmsApi sms_api.SmsAPI
|
||||
}
|
||||
|
||||
// Api new函数实例化,实例化完成后会返回结构体地指针类型
|
||||
|
@@ -3,32 +3,359 @@ package captcha_api
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wenlng/go-captcha/v2/base/option"
|
||||
ginI18n "github.com/gin-contrib/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/wenlng/go-captcha-assets/helper"
|
||||
"github.com/wenlng/go-captcha/v2/click"
|
||||
"github.com/wenlng/go-captcha/v2/rotate"
|
||||
"github.com/wenlng/go-captcha/v2/slide"
|
||||
"log"
|
||||
"schisandra-cloud-album/api/captcha_api/model"
|
||||
"schisandra-cloud-album/common/redis"
|
||||
"schisandra-cloud-album/common/result"
|
||||
"schisandra-cloud-album/global"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GenerateTextCaptcha 生成文本验证码
|
||||
func GenerateTextCaptcha() {
|
||||
captData, err := global.TextCaptcha.Generate()
|
||||
// GenerateRotateCaptcha 生成旋转验证码
|
||||
// @Summary 生成旋转验证码
|
||||
// @Description 生成旋转验证码
|
||||
// @Tags 旋转验证码
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/rotate/get [get]
|
||||
func (CaptchaAPI) GenerateRotateCaptcha(c *gin.Context) {
|
||||
captchaData, err := global.RotateCaptcha.Generate()
|
||||
if err != nil {
|
||||
global.LOG.Fatalln(err)
|
||||
}
|
||||
blockData := captchaData.GetData()
|
||||
if blockData == nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
var masterImageBase64, thumbImageBase64 string
|
||||
masterImageBase64 = captchaData.GetMasterImage().ToBase64()
|
||||
thumbImageBase64 = captchaData.GetThumbImage().ToBase64()
|
||||
dotsByte, err := json.Marshal(blockData)
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
key := helper.StringToMD5(string(dotsByte))
|
||||
err = redis.Set(key, dotsByte, time.Minute).Err()
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
bt := map[string]interface{}{
|
||||
"key": key,
|
||||
"image": masterImageBase64,
|
||||
"thumb": thumbImageBase64,
|
||||
}
|
||||
result.OkWithData(bt, c)
|
||||
}
|
||||
|
||||
// CheckRotateData 验证旋转验证码
|
||||
// @Summary 验证旋转验证码
|
||||
// @Description 验证旋转验证码
|
||||
// @Tags 旋转验证码
|
||||
// @Param angle query string true "验证码角度"
|
||||
// @Param key query string true "验证码key"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/rotate/check [post]
|
||||
func (CaptchaAPI) CheckRotateData(c *gin.Context) {
|
||||
rotateRequest := model.RotateCaptchaRequest{}
|
||||
err := c.ShouldBindJSON(&rotateRequest)
|
||||
angle := rotateRequest.Angle
|
||||
key := rotateRequest.Key
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
cacheDataByte, err := redis.Get(key).Bytes()
|
||||
if len(cacheDataByte) == 0 || err != nil {
|
||||
result.FailWithCodeAndMessage(1011, ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||
return
|
||||
}
|
||||
var dct *rotate.Block
|
||||
if err := json.Unmarshal(cacheDataByte, &dct); err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
sAngle, _ := strconv.ParseFloat(fmt.Sprintf("%v", angle), 64)
|
||||
chkRet := rotate.CheckAngle(int64(sAngle), int64(dct.Angle), 2)
|
||||
if chkRet {
|
||||
result.OkWithMessage("success", c)
|
||||
return
|
||||
}
|
||||
result.FailWithMessage("fail", c)
|
||||
}
|
||||
|
||||
// GenerateBasicTextCaptcha 生成基础文字验证码
|
||||
// @Summary 生成基础文字验证码
|
||||
// @Description 生成基础文字验证码
|
||||
// @Tags 基础文字验证码
|
||||
// @Param type query string true "验证码类型"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/text/get [get]
|
||||
func (CaptchaAPI) GenerateBasicTextCaptcha(c *gin.Context) {
|
||||
var capt click.Captcha
|
||||
if c.Query("type") == "light" {
|
||||
capt = global.LightTextCaptcha
|
||||
} else {
|
||||
capt = global.TextCaptcha
|
||||
}
|
||||
captData, err := capt.Generate()
|
||||
if err != nil {
|
||||
global.LOG.Fatalln(err)
|
||||
}
|
||||
dotData := captData.GetData()
|
||||
if dotData == nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
var masterImageBase64, thumbImageBase64 string
|
||||
masterImageBase64 = captData.GetMasterImage().ToBase64()
|
||||
thumbImageBase64 = captData.GetThumbImage().ToBase64()
|
||||
|
||||
dotsByte, err := json.Marshal(dotData)
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
key := helper.StringToMD5(string(dotsByte))
|
||||
err = redis.Set(key, dotsByte, time.Minute).Err()
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
bt := map[string]interface{}{
|
||||
"key": key,
|
||||
"image": masterImageBase64,
|
||||
"thumb": thumbImageBase64,
|
||||
}
|
||||
result.OkWithData(bt, c)
|
||||
}
|
||||
|
||||
// CheckClickData 验证基础文字验证码
|
||||
// @Summary 验证基础文字验证码
|
||||
// @Description 验证基础文字验证码
|
||||
// @Tags 基础文字验证码
|
||||
// @Param captcha query string true "验证码"
|
||||
// @Param key query string true "验证码key"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/text/check [get]
|
||||
func (CaptchaAPI) CheckClickData(c *gin.Context) {
|
||||
dots := c.Query("dots")
|
||||
key := c.Query("key")
|
||||
if dots == "" || key == "" {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
cacheDataByte, err := redis.Get(key).Bytes()
|
||||
if len(cacheDataByte) == 0 || err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
src := strings.Split(dots, ",")
|
||||
|
||||
var dct map[int]*click.Dot
|
||||
if err := json.Unmarshal(cacheDataByte, &dct); err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
chkRet := false
|
||||
if (len(dct) * 2) == len(src) {
|
||||
for i := 0; i < len(dct); i++ {
|
||||
dot := dct[i]
|
||||
j := i * 2
|
||||
k := i*2 + 1
|
||||
sx, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[j]), 64)
|
||||
sy, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[k]), 64)
|
||||
|
||||
chkRet = click.CheckPoint(int64(sx), int64(sy), int64(dot.X), int64(dot.Y), int64(dot.Width), int64(dot.Height), 0)
|
||||
if !chkRet {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if chkRet {
|
||||
result.OkWithMessage("success", c)
|
||||
return
|
||||
}
|
||||
result.FailWithMessage("fail", c)
|
||||
}
|
||||
|
||||
// GenerateClickShapeCaptcha 生成点击形状验证码
|
||||
// @Summary 生成点击形状验证码
|
||||
// @Description 生成点击形状验证码
|
||||
// @Tags 点击形状验证码
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/shape/get [get]
|
||||
func (CaptchaAPI) GenerateClickShapeCaptcha(c *gin.Context) {
|
||||
captData, err := global.ClickShapeCaptcha.Generate()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
dotData := captData.GetData()
|
||||
if dotData == nil {
|
||||
log.Fatalln(">>>>> generate err")
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
var masterImageBase64, thumbImageBase64 string
|
||||
masterImageBase64 = captData.GetMasterImage().ToBase64()
|
||||
thumbImageBase64 = captData.GetThumbImage().ToBase64()
|
||||
|
||||
dots, _ := json.Marshal(dotData)
|
||||
fmt.Println(">>>>> ", string(dots))
|
||||
|
||||
err = captData.GetMasterImage().SaveToFile("./.caches/master.jpg", option.QualityNone)
|
||||
dotsByte, err := json.Marshal(dotData)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
err = captData.GetThumbImage().SaveToFile("./.caches/thumb.png")
|
||||
key := helper.StringToMD5(string(dotsByte))
|
||||
err = redis.Set(key, dotsByte, time.Minute).Err()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
bt := map[string]interface{}{
|
||||
"key": key,
|
||||
"image": masterImageBase64,
|
||||
"thumb": thumbImageBase64,
|
||||
}
|
||||
result.OkWithData(bt, c)
|
||||
}
|
||||
|
||||
// GenerateSlideBasicCaptData 验证点击形状验证码
|
||||
// @Summary 验证点击形状验证码
|
||||
// @Description 验证点击形状验证码
|
||||
// @Tags 点击形状验证码
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/shape/check [get]
|
||||
func (CaptchaAPI) GenerateSlideBasicCaptData(c *gin.Context) {
|
||||
captData, err := global.SlideCaptcha.Generate()
|
||||
if err != nil {
|
||||
global.LOG.Fatalln(err)
|
||||
}
|
||||
blockData := captData.GetData()
|
||||
if blockData == nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
var masterImageBase64, tileImageBase64 string
|
||||
masterImageBase64 = captData.GetMasterImage().ToBase64()
|
||||
|
||||
tileImageBase64 = captData.GetTileImage().ToBase64()
|
||||
|
||||
dotsByte, err := json.Marshal(blockData)
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
key := helper.StringToMD5(string(dotsByte))
|
||||
err = redis.Set(key, dotsByte, time.Minute).Err()
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
bt := map[string]interface{}{
|
||||
"key": key,
|
||||
"image": masterImageBase64,
|
||||
"tile": tileImageBase64,
|
||||
"tile_width": blockData.Width,
|
||||
"tile_height": blockData.Height,
|
||||
"tile_x": blockData.TileX,
|
||||
"tile_y": blockData.TileY,
|
||||
}
|
||||
result.OkWithData(bt, c)
|
||||
}
|
||||
|
||||
// CheckSlideData 验证点击形状验证码
|
||||
// @Summary 验证点击形状验证码
|
||||
// @Description 验证点击形状验证码
|
||||
// @Tags 点击形状验证码
|
||||
// @Param point query string true "点击坐标"
|
||||
// @Param key query string true "验证码key"
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/shape/slide/check [get]
|
||||
func (CaptchaAPI) CheckSlideData(c *gin.Context) {
|
||||
point := c.Query("point")
|
||||
key := c.Query("key")
|
||||
if point == "" || key == "" {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
|
||||
cacheDataByte, err := redis.Get(key).Bytes()
|
||||
if len(cacheDataByte) == 0 || err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
src := strings.Split(point, ",")
|
||||
|
||||
var dct *slide.Block
|
||||
if err := json.Unmarshal(cacheDataByte, &dct); err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
|
||||
chkRet := false
|
||||
if 2 == len(src) {
|
||||
sx, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[0]), 64)
|
||||
sy, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[1]), 64)
|
||||
chkRet = slide.CheckPoint(int64(sx), int64(sy), int64(dct.X), int64(dct.Y), 4)
|
||||
}
|
||||
|
||||
if chkRet {
|
||||
result.OkWithMessage("success", c)
|
||||
return
|
||||
}
|
||||
result.FailWithMessage("fail", c)
|
||||
}
|
||||
|
||||
// GenerateSlideRegionCaptData 验证点击形状验证码
|
||||
// @Summary 验证点击形状验证码
|
||||
// @Description 验证点击形状验证码
|
||||
// @Tags 点击形状验证码
|
||||
// @Success 200 {string} json
|
||||
// @Router /api/captcha/shape/slide/region/get [get]
|
||||
func (CaptchaAPI) GenerateSlideRegionCaptData(c *gin.Context) {
|
||||
captData, err := global.SlideRegionCaptcha.Generate()
|
||||
if err != nil {
|
||||
global.LOG.Fatalln(err)
|
||||
}
|
||||
|
||||
blockData := captData.GetData()
|
||||
if blockData == nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
|
||||
var masterImageBase64, tileImageBase64 string
|
||||
masterImageBase64 = captData.GetMasterImage().ToBase64()
|
||||
tileImageBase64 = captData.GetTileImage().ToBase64()
|
||||
|
||||
blockByte, err := json.Marshal(blockData)
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
key := helper.StringToMD5(string(blockByte))
|
||||
err = redis.Set(key, blockByte, time.Minute).Err()
|
||||
if err != nil {
|
||||
result.FailWithNull(c)
|
||||
return
|
||||
}
|
||||
bt := map[string]interface{}{
|
||||
"code": 0,
|
||||
"key": key,
|
||||
"image": masterImageBase64,
|
||||
"tile": tileImageBase64,
|
||||
"tile_width": blockData.Width,
|
||||
"tile_height": blockData.Height,
|
||||
"tile_x": blockData.TileX,
|
||||
"tile_y": blockData.TileY,
|
||||
}
|
||||
result.OkWithData(bt, c)
|
||||
}
|
||||
|
6
api/captcha_api/model/request_model.go
Normal file
6
api/captcha_api/model/request_model.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
type RotateCaptchaRequest struct {
|
||||
Angle int `json:"angle"`
|
||||
Key string `json:"key"`
|
||||
}
|
3
api/sms_api/sms.go
Normal file
3
api/sms_api/sms.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package sms_api
|
||||
|
||||
type SmsAPI struct{}
|
80
api/sms_api/sms_api.go
Normal file
80
api/sms_api/sms_api.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package sms_api
|
||||
|
||||
import (
|
||||
ginI18n "github.com/gin-contrib/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
gosms "github.com/pkg6/go-sms"
|
||||
"github.com/pkg6/go-sms/gateways"
|
||||
"github.com/pkg6/go-sms/gateways/aliyun"
|
||||
"github.com/pkg6/go-sms/gateways/smsbao"
|
||||
"schisandra-cloud-album/common/result"
|
||||
"schisandra-cloud-album/global"
|
||||
"schisandra-cloud-album/utils"
|
||||
)
|
||||
|
||||
// SendMessageByAli 发送短信验证码
|
||||
// @Summary 发送短信验证码
|
||||
// @Description 发送短信验证码
|
||||
// @Tags 短信验证码
|
||||
// @Produce json
|
||||
// @Param phone query string true "手机号"
|
||||
// @Router /api/sms/ali/send [get]
|
||||
func (SmsAPI) SendMessageByAli(c *gin.Context) {
|
||||
phone := c.Query("phone")
|
||||
if phone == "" {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotEmpty"), c)
|
||||
return
|
||||
}
|
||||
sms := gosms.NewParser(gateways.Gateways{
|
||||
ALiYun: aliyun.ALiYun{
|
||||
Host: global.CONFIG.SMS.Ali.Host,
|
||||
AccessKeyId: global.CONFIG.SMS.Ali.AccessKeyID,
|
||||
AccessKeySecret: global.CONFIG.SMS.Ali.AccessKeySecret,
|
||||
},
|
||||
})
|
||||
code := utils.GenValidateCode(6)
|
||||
_, err := sms.Send(phone, gosms.MapStringAny{
|
||||
"content": "您的验证码是:****。请不要把验证码泄露给其他人。",
|
||||
"template": global.CONFIG.SMS.Ali.TemplateID,
|
||||
//"signName": global.CONFIG.SMS.Ali.Signature,
|
||||
"data": gosms.MapStrings{
|
||||
"code": code,
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
global.LOG.Error(err)
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendFailed"), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SendMessageBySmsBao 短信宝发送短信验证码
|
||||
// @Summary 短信宝发送短信验证码
|
||||
// @Description 发送短信验证码
|
||||
// @Tags 短信验证码
|
||||
// @Produce json
|
||||
// @Param phone query string true "手机号"
|
||||
// @Router /api/sms/smsbao/send [get]
|
||||
func (SmsAPI) SendMessageBySmsBao(c *gin.Context) {
|
||||
phone := c.Query("phone")
|
||||
if phone == "" {
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotEmpty"), c)
|
||||
return
|
||||
}
|
||||
sms := gosms.NewParser(gateways.Gateways{
|
||||
SmsBao: smsbao.SmsBao{
|
||||
User: global.CONFIG.SMS.SmsBao.User,
|
||||
Password: global.CONFIG.SMS.SmsBao.Password,
|
||||
},
|
||||
})
|
||||
code := utils.GenValidateCode(6)
|
||||
_, err := sms.Send(phone, gosms.MapStringAny{
|
||||
"content": "您的验证码是:" + code + "。请不要把验证码泄露给其他人。",
|
||||
}, nil)
|
||||
if err != nil {
|
||||
global.LOG.Error(err)
|
||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendFailed"), c)
|
||||
return
|
||||
}
|
||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "CaptchaSendSuccess"), c)
|
||||
}
|
Reference in New Issue
Block a user