前言

为了区分访问者是否为机器人,我们常添加验证码来达到此目的。这里我们使用Google的reCaptchaV3和reCaptchaV2结合来做认证。同时也可以起到CSRF防御的功能!

「其他分享」快速接入Google 验证码实现登录验证插图
快速接入Google 验证码实现登录验证

申请令牌

谷歌云

配置前端

这里我们前端使用的是VUE

#前期我们需要把V2的验证方法给隐藏起来,使用V3来验证更方便
import reCaptcha from '../layouts/google.vue'
...
login: async function () {
      const csrf = getCookie('csrf')
      const _this = this
      if (csrf !== '') {
        if (this.username === '' || this.password === '') {
          this.isError = true
          this.errorInfo = '用户名或密码你没填呢!'
        } else {
          if (this.token === '' && this.googleV3) {
            this.isError = true
            this.errorInfo = '验证码你没有填写呢!'
            return
          }
          this.visible = true
          const v3token = await this.GetV3Token()
          console.log(v3token)
          this.$axios.post('api/login', {
            username: this.username,
            password: this.$md5(this.password),
            csrfToken: csrf,
            google: this.token,
            rememberMe: this.rememberMe,
            googleV3: v3token
          }).then(function (response) {
            if (response.data.code === 1) {
              _this.$q.notify({
                type: 'positive',
                position: 'top',
                message: response.data.info,
                icon: 'fa fa-check'
              })
              _this.Jump(1)
            } else if (response.data.code === 3) {
              _this.isError = true
              _this.googleV3 = true
              _this.errorInfo = response.data.info
              _this.passwordErr = false
            } else {
              _this.isError = true
              _this.errorInfo = response.data.info
              _this.passwordErr = true
            }
          }).catch(function (error) {
            _this.isError = true
            _this.errorInfo = error.message
            _this.passwordErr = false
          }).finally(function () {
            _this.visible = false
          })
        }
      } else {
        this.isError = true
        this.errorInfo = 'CSRF令牌获取失败, 无法登录!'
        this.passwordErr = false
      }
    },
    # 获取v3的token函数
    GetV3Token: async function () {
      let returnToken = ''
      await this.$recaptcha('login').then((token) => {
        returnToken = token
      })
      console.log('tokenV3 ' + returnToken)
      return returnToken
    }
  },
  ...

后端

app.Post("api/login", func(ctx iris.Context) {
        username := ctx.FormValue("username")
        password := ctx.FormValue("password")
        //rememberMe := ctx.FormValue("rememberMe")
        token := ctx.FormValue("google")
        tokenV3 := ctx.FormValue("googleV3")
        // "记住我"功能未实现!
        if username != "" && password != "" {
            if token == "" {
                if !methods.VerifyGoogleV3(tokenV3) {
                    _, _ = ctx.JSON(methods.ReturnPack{Code: 3, Info: "检测到你可能是机器人,请输入验证码!"})
                    return
                }
            } else {
                if !methods.VerifyGoogleV2(token) {
                    _, _ = ctx.JSON(methods.ReturnPack{Code: 0, Info: "验证码验证出现问题!"})
                    return
                }
            }
 
            user, err := methods.VerifyUsernamePassword(username, password, ctx.RemoteAddr())
 
            if err == nil {
                if user.Role == -1 {
                    _, _ = ctx.JSON(methods.ReturnPack{Code: 0, Info: "账号已被封禁, 请联系网站管理员!"})
                    return
                }
                if user.SessId != "" {
                    sysinit.Sess.DestroyByID(user.SessId)
                }
                s := sysinit.Sess.Start(ctx)
                s.Set("auth", true)
                s.Set("id", user.Id)
                s.Set("role", user.Role)
                s.Set("username", user.Username)
                _ = methods.SetSessId(user, s.ID())
                //log.Info(rememberMe)
                _, _ = ctx.JSON(methods.ReturnPack{Code: 1, Info: "登录成功,欢迎你" + user.Username + "正在为你跳转!"})
            } else {
                _, _ = ctx.JSON(methods.ReturnPack{Code: 0, Info: err.Error()})
            }
        } else {
            _, _ = ctx.JSON(methods.ReturnPack{Code: 0, Info: "你未填写用户名或密码"})
        }
    })

相关验证函数

// 验证谷歌验证码V2
func VerifyGoogleV2(token string) bool {
    res, err := requests.Get("https://recaptcha.net/recaptcha/api/siteverify", requests.Params{"secret": config.Sysconfig.RecaptchaSecretV2, "response": token})
    if err != nil {
        log.Error(err.Error())
        return false
    }
    log.Info(res.Text())
    var googleBack GoogleBack
    err = res.Json(&googleBack)
    if err != nil {
        log.Error(err.Error())
        return false
    }
    return googleBack.Success
}
 
// 验证谷歌验证码V3
func VerifyGoogleV3(token string) bool {
    res, err := requests.Get("https://recaptcha.net/recaptcha/api/siteverify", requests.Params{"secret": config.Sysconfig.RecaptchaSecretV3, "response": token})
    if err != nil {
        log.Error(err.Error())
        return false
    }
    log.Info(res.Text())
    var googleBackV3 GoogleBackV3
    err = res.Json(&googleBackV3)
    if err != nil {
        log.Error(err.Error())
        return false
    }
    if googleBackV3.Score > 0.5 {
        return true
    } else {
        return false
    }
}

结果

[+] [信息] [2021-03-05 10:38:12]: {
  "success": true,
  "challenge_ts": "2021-03-05T02:32:06Z",
  "hostname": "localhost",
  "score": 0.9,
  "action": "login"
}

此时我们就可以做到在v3验证失败的情况下调起v2来辅助验证!验证码功能成功实现.