gin-图形验证码
文档地址:https://mojotv.cn/go/refactor-base64-captcha
1. 在api接口文件中配置生成验证码的代码
在user-web/api下面创建chaptcha.go文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package api //导入 import ( "github.com/gin-gonic/gin" "github.com/mojocn/base64Captcha" "go.uber.org/zap" "net/http" ) //文档:https://mojotv.cn/go/refactor-base64-captcha // 做验证码保存 var store = base64Captcha.DefaultMemStore func GetCaptcha(ctx *gin.Context) { driver := base64Captcha.NewDriverDigit(240, 80, 5, 0.7, 80) cp := base64Captcha.NewCaptcha(driver, store) id, b64s, err := cp.Generate() if err != nil { zap.S().Errorf( "生成验证码错误,: " , err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "msg" : "生成验证码错误" , }) return } ctx.JSON(http.StatusOK, gin.H{ "captchaId" :id, "picPath" : b64s, }) } |
2. 添加路由
在user-web/route下面创建base.go文件
1 2 3 4 5 6 7 8 9 10 11 12 13 | package router import ( "github.com/gin-gonic/gin" "mxshop-api/user-web/api" ) func InitBaseRoute(Route *gin.RouterGroup) { BaseRoute := Route.Group( "base" ) { BaseRoute.GET( "captcha" , api.GetCaptcha) } } |
3. 初始化路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package initialize import ( "github.com/gin-gonic/gin" "mxshop-api/user-web/middlewares" "mxshop-api/user-web/router" ) func Routers() *gin.Engine { Router := gin.Default() //配置跨域 Router.Use(middlewares.Cors()) ApiGroup := Router.Group( "v1" ) router.InitUserRoute(ApiGroup) router.InitBaseRoute(ApiGroup) return Router } |
4. 请求接口
http://127.0.0.1:8021/v1/base/captcha
1 | { "captchaId" : "Y2C3XDKk92GQExz44SqM" , "picPath" : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAADwCAMAAACg0xNxAAAA81BMVEUAAAB+MGtwIl10JmFyJF9qHFdnGVS/cayIOnWCNG+NP3qsXpldD0qAMm2cTolSBD99L2qxY56gUo2MPnlpG1aiVI+dT4qzZaB6LGfFd7KoWpVaDEejVZB3KWSURoHNf7rKfLekVpG6bKePQXyxY55uIFtvIVxzJWCHOXRPATx8LmmfUYxkFlF7LWiSRH+KPHeIOnVnGVRhE06KPHfEdrF6LGeSRH/OgLtqHFdRAz5xI16HOXRVB0LCdK93KWSyZJ+bTYhjFVBlF1JcDkm1Z6KrXZhwIl2CNG9XCURyJF+8bqmRQ358Lml7LWiwYp1iFE+dT4rWbgCSAAAAAXRSTlMAQObYZgAAA6VJREFUeJzs21lL60AUB/CJIFwXWn0Q8cEd+lBQXFCjVUEpoqDf/+tcbNLMdpI5M+cft87/wdt2Or+epGluMpmo35mT4DuuuxpfPe8kJF5fd4ivr74Y8GIrROZBDbHew3AIFsEV9pOigGJ1cGDNVs8w6NQoFFHrdDqdOYVeA+IKtYEo1OrerNNEs+o361sQLc2qiAMr0qqoesJc/jPKNLad+YuNHADPzgixfqDUQDOqebFesyu8CnUBRVEMBoO5Ym3yn39XVtpE1zMfDMwVqFtn//A8B1TuplPYbe2Mt8DKWHLdZqyPUF3Or9f6Xq338UBjieZ/vAIdMQC6sn7qNfMqtHv5BclAs3/bK8mg27G1xL14u7PEvb14cV6h9f9OeoXu7ssBL+M9VZj7X9e7TBEdubPCHarjVZfY/Zk7O4R4ddUqhhOs8DTdJnN6ChfB3u/MLdq7hYtgz0747CvSC54fRotgb3GyhvbW4CLjPfsxIOOgc39/Jh75LbuExzksrrwjT9zdpcSwV4dXYU4OJ5JjNtITHQWSItjLyfm2HKK9Q0dcFouOtywWlT0WvXwj96zR8psbgGg9k3t95jm6hzv653jPsaI3PumKkV6owkXLP7T3Dy6CvRwnq2hvFS6CPXHG1eU1nDceq+lUJA7VgSVeXMgqHA4PDizxQqIpv8I/mffm0TbGe5+L29sgsXmE8XKI3KG9u0Y8BomNdwwSm1AecrKEgUKYEkyWZWk9ly97KevOSj/fEF7so0x4etrcv2zJ0/df9JIb+69E1BMlnoLNYCJdumEkMn10NKJE/hCmnhxUXbJXakR8Ttwga6Fda+bRJKnC9kwmE4BiiWDva9J5YpRw2bLz1C3pwiq6QlHIORNNzjva6B8vOatDe+ftIv3jxVeYw0yPR+y8vATFSO8lKEYG7fHyhvbe4CLYy1nIbKG9rWjxKSBGe08BMTpy72/sTzbkH2PdtrSxIRadG6vQFeb87EAn5t2Cp/rN5sSiK8xBBj3GWZYleIJ5yZoCHzeixPDgY1S/YPz0iyK4/eIzS54nuaFDqaUlX+T0a9+IPI+VfKeHTtoa7PD871jVEwpSRdIbM0X2sBHXaxnYSk8vA1ubaG8TLoK9v5AfcIocEsHeT0toBk/snBJrjtE94UXPejG9e0qM9KwQXs5i5gPtfcDFtoZvvCuPeTjF96rDKeCFxMqzLnU+Srx6grrlPQpEcgo9osJFyDraW4eL+uH/AAAA///Hyj6PdBcIyQAAAABJRU5ErkJggg==" } |
5. 验证
在密码验证之前先对验证码进行验证
在api/user.go中PassWordLogin配置
1 2 3 4 5 6 | if !store.Verify(passWordLoginForm.CaptchaId, passWordLoginForm.Captcha, true) { ctx.JSON(http.StatusBadRequest, gin.H{ "captcha" : "验证码错误" , }) return } |
全部内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | unc PassWordLogin(ctx *gin.Context) { //密码登录的接口 //1.表单验证, 在forms中定义 //ctx.JSON(http.StatusOK, "密码登录") passWordLoginForm := forms.PassWordLoginForm{} //固定格式 if err := ctx.ShouldBind(&passWordLoginForm); err != nil { HandleValidatorError(ctx, err) return } if !store.Verify(passWordLoginForm.CaptchaId, passWordLoginForm.Captcha, true) { ctx.JSON(http.StatusBadRequest, gin.H{ "captcha" : "验证码错误" , }) return } //拨号连接用户RPC服务 userConn, err := grpc.Dial(fmt.Sprintf( "%s:%d" , global.ServerConfig.UserSrvInfo.Host, global.ServerConfig.UserSrvInfo.Port), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { zap.S().Errorw( "[GetUserList]连接失败【用户服务失败】" , "msg" , err.Error()) } //生成grpc的client并调用接口 userSrvClient := proto.NewUserClient(userConn) // 登录的逻辑, 查询是否存在,密码是否相等 if rsp, err := userSrvClient.GetUserByMobile(context.Background(), &proto.MobileRequests{ Mobile: passWordLoginForm.Mobile, }); err != nil { if e, ok := status.FromError(err); ok { switch e.Code() { case codes.NotFound: ctx.JSON(http.StatusBadRequest, map [string]string{ "mobile" : "用户不存在" , }) default : ctx.JSON(http.StatusBadRequest, map [string]string{ "mobile" : "登录失败" , }) } return } } else { // 只是查询了用户而已,并没有检查密码 if passRsp, pasErr := userSrvClient.CheckPassWord(context.Background(), &proto.PasswordCheckInfo{ Password: passWordLoginForm.PassWord, EncryptedPassword: rsp.PassWord, }); pasErr != nil { ctx.JSON(http.StatusInternalServerError, map [string]string{ "password" : "登录失败" , }) } else { if passRsp.Success { //生成token j := middlewares.NewJWT() claims := models.CustomClaims{ ID: uint(rsp.Id), NickName: rsp.NickName, AuthorityId: uint(rsp.Role), StandardClaims: jwt.StandardClaims{ NotBefore: time.Now().Unix(), //签名的生效时间 ExpiresAt: time.Now().Unix() + 60*60*24*30, //30day过期 Issuer: "wanghui" , }, } token, err := j.CreateToken(claims) if err != nil { ctx.JSON(http.StatusInternalServerError, map [string]string{ "msg" : "生成token失败" , }) return } ctx.JSON(http.StatusOK, gin.H{ "id" : rsp.Id, "nick_name" : rsp.NickName, "token" : token, "expired_at" : time.Now().Unix() + 60*60*24*30*1000, }) } else { ctx.JSON(http.StatusBadRequest, map [string]string{ "msg" : "登录失败" , }) } } } } |
修改PassWordLoginForm验证规则
1 2 3 4 5 6 7 8 | package forms type PassWordLoginForm struct { Mobile string `form: "mobile" json: "mobile" binding: "required,mobile" ` //手机号码规则验证,自定义validator PassWord string `form: "password" json: "password" binding: "required,min=3,max=20" ` Captcha string `form: "captcha" json: "captcha" binding: "required,min=5,max=5" ` CaptchaId string `form: "captcha_id" json: "captcha_id" binding: "required,min=5" ` } |