最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go + Vue 實現(xiàn)行為驗證碼完整指南

 更新時間:2026年04月01日 08:57:16   作者:牛奔  
本文介紹如何使用go-captcha庫在Go后端和Vue前端實現(xiàn)滑動驗證碼功能,核心流程包括驗證碼生成、驗證、安全性說明及常見問題解決方法,感興趣的朋友跟隨小編一起看看吧

前言

在現(xiàn)代 Web 應用中,驗證碼是防止機器人攻擊和惡意請求的重要手段。相比傳統(tǒng)的圖形驗證碼,滑動行為驗證碼具有更好的用戶體驗。本文將介紹如何使用 go-captcha 庫在 Go 后端和 Vue 前端實現(xiàn)滑動驗證碼功能。

技術棧

  • 后端:Go + Gin 框架
  • 前端:Vue 3 + Element Plus
  • 驗證碼庫:go-captcha(后端)+ go-captcha-vue(前端)
  • 緩存:Redis(用于存儲驗證碼數(shù)據(jù))

一、后端實現(xiàn)

1.1 安裝依賴

go get github.com/wenlng/go-captcha/v2
go get github.com/wenlng/go-captcha-assets
go get github.com/gin-gonic/gin
go get github.com/redis/go-redis/v9

1.2 初始化驗證碼模塊

創(chuàng)建 captcha/init.go 文件:

package captcha
import (
	"errors"
)
var (
	ErrGenData = errors.New("generate data error")
)
const (
	Deviation = 10  // 驗證偏差值,允許用戶滑動有一定誤差
)
func Init() error {
	return initSlide()
}

1.3 實現(xiàn)滑動驗證碼核心邏輯

創(chuàng)建 captcha/slide.go 文件:

package captcha
import (
	images "github.com/wenlng/go-captcha-assets/resources/imagesv2"
	"github.com/wenlng/go-captcha-assets/resources/tiles"
	"github.com/wenlng/go-captcha/v2/base/option"
	"github.com/wenlng/go-captcha/v2/slide"
)
var slideCapt slide.Captcha
// initSlide 初始化滑動驗證碼
func initSlide() error {
	builder := slide.NewBuilder(
		slide.WithGenGraphNumber(1),
		slide.WithEnableGraphVerticalRandom(true),
		slide.WithImageSize(option.Size{Width: 300, Height: 220}),
	)
	// 加載背景圖片資源
	imgs, err := images.GetImages()
	if err != nil {
		return err
	}
	// 加載滑塊圖形資源
	graphs, err := tiles.GetTiles()
	if err != nil {
		return err
	}
	var newGraphs = make([]*slide.GraphImage, 0, len(graphs))
	for i := range graphs {
		graph := graphs[i]
		newGraphs = append(newGraphs, &slide.GraphImage{
			OverlayImage: graph.OverlayImage,
			MaskImage:    graph.MaskImage,
			ShadowImage:  graph.ShadowImage,
		})
	}
	// 設置資源
	builder.SetResources(
		slide.WithGraphImages(newGraphs),
		slide.WithBackgrounds(imgs),
	)
	slideCapt = builder.Make()
	return nil
}
// Slide 驗證碼數(shù)據(jù)結構
type Slide struct {
	// 滑塊初始顯示坐標
	SliderX      int
	SliderY      int
	SliderWidth  int
	SliderHeight int
	// 整體大小
	MainWidth  int
	MainHeight int
	// 答案坐標(服務端保存,不返回給前端)
	X int
	Y int
	// 主圖與滑塊的圖片,base64編碼
	MainImage   string
	SliderImage string
}
// NewSlide 生成新的滑動驗證碼
func NewSlide() (*Slide, error) {
	m := &Slide{}
	captData, err := slideCapt.Generate()
	if err != nil {
		return nil, err
	}
	dotData := captData.GetData()
	if dotData == nil {
		return nil, ErrGenData
	}
	// 答案坐標(正確的滑塊位置)
	m.X = dotData.X
	m.Y = dotData.Y
	// 滑塊初始顯示坐標
	m.SliderWidth = dotData.Width
	m.SliderHeight = dotData.Height
	m.SliderX = dotData.DX
	m.SliderY = dotData.DY
	// 圖片大小
	m.MainWidth = 300
	m.MainHeight = 220
	// 轉換為 base64 編碼
	m.MainImage, err = captData.GetMasterImage().ToBase64()
	if err != nil {
		return nil, err
	}
	m.SliderImage, err = captData.GetTileImage().ToBase64()
	if err != nil {
		return nil, err
	}
	return m, nil
}
// VerifySlide 驗證滑動位置是否正確
func VerifySlide(userX, userY, slideX, slideY int) bool {
	return slide.Validate(userX, userY, slideX, slideY, Deviation)
}

1.4 定義響應結構體

創(chuàng)建 vo/captcha.go 文件:

package vo
type GetCaptchaRes struct {
	ID           string `json:"id"`           // 驗證碼唯一標識
	SliderX      int    `json:"sliderX"`      // 滑塊初始X坐標
	SliderY      int    `json:"sliderY"`      // 滑塊初始Y坐標
	SliderWidth  int    `json:"sliderWidth"`  // 滑塊寬度
	SliderHeight int    `json:"sliderHeight"` // 滑塊高度
	SliderImage  string `json:"sliderImage"`  // 滑塊圖片(base64)
	MainWidth    int    `json:"mainWidth"`    // 主圖寬度
	MainHeight   int    `json:"mainHeight"`   // 主圖高度
	MainImage    string `json:"mainImage"`    // 主圖(base64)
}

1.5 實現(xiàn) HTTP 處理器

創(chuàng)建 handler/captcha_handler.go 文件:

package handler
import (
	"encoding/json"
	"time"
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"github.com/rs/xid"
	"your-project/captcha"
	"your-project/vo"
)
type CaptchaHandler struct {
	redis *redis.Client
}
func NewCaptchaHandler(redis *redis.Client) *CaptchaHandler {
	return &CaptchaHandler{
		redis: redis,
	}
}
// GetCaptcha 獲取驗證碼
func (h *CaptchaHandler) GetCaptcha(c *gin.Context) {
	// 生成驗證碼
	m, err := captcha.NewSlide()
	if err != nil {
		c.JSON(500, gin.H{"error": "Failed to create captcha"})
		return
	}
	// 序列化驗證碼數(shù)據(jù)
	value, err := json.Marshal(m)
	if err != nil {
		c.JSON(500, gin.H{"error": "Failed to marshal captcha"})
		return
	}
	// 生成唯一ID并存儲到 Redis,有效期1分鐘
	uuid := xid.New().String()
	err = h.redis.Set(c, uuid, value, time.Minute).Err()
	if err != nil {
		c.JSON(500, gin.H{"error": "Failed to store captcha"})
		return
	}
	// 返回給前端的數(shù)據(jù)(不包含答案坐標)
	res := &vo.GetCaptchaRes{
		ID:           uuid,
		SliderX:      m.SliderX,
		SliderY:      m.SliderY,
		SliderWidth:  m.SliderWidth,
		SliderHeight: m.SliderHeight,
		SliderImage:  m.SliderImage,
		MainWidth:    m.MainWidth,
		MainHeight:   m.MainHeight,
		MainImage:    m.MainImage,
	}
	c.JSON(200, gin.H{"code": 0, "data": res})
}
// VerifyCaptcha 驗證滑動驗證碼
func (h *CaptchaHandler) VerifyCaptcha(c *gin.Context) {
	var data struct {
		ID string `json:"id"` // 驗證碼標識
		X  int    `json:"x"`  // 用戶滑動的X坐標
		Y  int    `json:"y"`  // 用戶滑動的Y坐標
	}
	if err := c.ShouldBindJSON(&data); err != nil {
		c.JSON(400, gin.H{"error": "Invalid arguments"})
		return
	}
	// 從 Redis 獲取驗證碼數(shù)據(jù)
	value, err := h.redis.Get(c, data.ID).Result()
	if err != nil {
		c.JSON(400, gin.H{"error": "驗證碼已過期或不存在"})
		return
	}
	// 反序列化驗證碼數(shù)據(jù)
	slide := captcha.Slide{}
	err = json.Unmarshal([]byte(value), &slide)
	if err != nil {
		c.JSON(500, gin.H{"error": "驗證碼數(shù)據(jù)錯誤"})
		return
	}
	// 驗證滑動位置
	if !captcha.VerifySlide(data.X, data.Y, slide.X, slide.Y) {
		c.JSON(400, gin.H{"error": "驗證碼驗證失敗"})
		return
	}
	// 驗證成功后刪除 Redis 中的數(shù)據(jù)(防止重復使用)
	h.redis.Del(c, data.ID)
	c.JSON(200, gin.H{"code": 0, "message": "驗證成功"})
}

1.6 注冊路由

main.go 中注冊路由:

package main
import (
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"your-project/captcha"
	"your-project/handler"
)
func main() {
	// 初始化驗證碼模塊
	if err := captcha.Init(); err != nil {
		panic(err)
	}
	// 初始化 Redis 客戶端
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	// 創(chuàng)建 Gin 路由
	r := gin.Default()
	// 創(chuàng)建處理器
	captchaHandler := handler.NewCaptchaHandler(rdb)
	// 注冊路由
	api := r.Group("/api")
	{
		api.POST("/captcha", captchaHandler.GetCaptcha)
		api.POST("/captcha/verify", captchaHandler.VerifyCaptcha)
	}
	r.Run(":8080")
}

二、前端實現(xiàn)

2.1 安裝依賴

npm install go-captcha-vue
npm install element-plus

2.2 創(chuàng)建驗證碼組件

創(chuàng)建 components/SlideCaptcha.vue 文件:

<template>
  <div class="slide-captcha-wrapper">
    <el-button 
      :disabled="!canSend" 
      @click="handleClick"
      class="trigger-btn"
    >
      {{ btnText }}
    </el-button>
    <!-- 滑動驗證碼彈窗 -->
    <el-dialog
      v-model="showDialog"
      width="326px"
      :close-on-click-modal="false"
      :show-close="false"
      :append-to-body="true"
    >
      <GoCaptchaSlide
        v-if="captchaData"
        :config="captchaConfig"
        :data="captchaData"
        :events="captchaEvents"
      />
    </el-dialog>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Slide as GoCaptchaSlide } from 'go-captcha-vue'
import { httpPost } from '@/utils/http'
// Props
const props = defineProps({
  btnText: {
    type: String,
    default: '發(fā)送驗證碼'
  }
})
// Emits
const emit = defineEmits(['success'])
// 狀態(tài)
const showDialog = ref(false)
const captchaData = ref(null)
const captchaKey = ref('')
const canSend = ref(true)
const btnText = ref(props.btnText)
// 滑動驗證碼配置
const captchaConfig = {
  width: 300,
  height: 220,
  thumbWidth: 60,
  thumbHeight: 60,
  showTheme: true,
  title: '請拖動滑塊完成驗證'
}
// 滑動驗證碼事件
const captchaEvents = {
  confirm: (point, reset) => {
    verifyCaptcha({ 
      x: Math.floor(point.x), 
      y: Math.floor(point.y) 
    })
    return false
  },
  refresh: () => {
    loadCaptcha()
  },
  close: () => {
    showDialog.value = false
  }
}
// 點擊按鈕觸發(fā)
const handleClick = () => {
  if (!canSend.value) return
  loadCaptcha()
}
// 加載滑動驗證碼
const loadCaptcha = () => {
  httpPost('/api/captcha')
    .then((res) => {
      captchaKey.value = res.data.id
      captchaData.value = {
        image: res.data.mainImage,
        thumb: res.data.sliderImage,
        thumbX: res.data.sliderX,
        thumbY: res.data.sliderY,
        thumbWidth: res.data.sliderWidth,
        thumbHeight: res.data.sliderHeight
      }
      showDialog.value = true
    })
    .catch((e) => {
      ElMessage.error('獲取驗證碼失?。? + e.message)
    })
}
// 驗證滑動驗證碼
const verifyCaptcha = (verifyData) => {
  httpPost('/api/captcha/verify', {
    id: captchaKey.value,
    x: verifyData.x,
    y: verifyData.y
  })
    .then(() => {
      showDialog.value = false
      ElMessage.success('驗證成功')
      emit('success')
    })
    .catch((e) => {
      ElMessage.error('驗證失?。? + e.message)
      // 驗證失敗,重新加載驗證碼
      captchaData.value = null
      loadCaptcha()
    })
}
// 暴露方法供父組件調(diào)用
defineExpose({
  loadCaptcha
})
</script>
<style scoped>
.slide-captcha-wrapper {
  display: inline-block;
}
.trigger-btn {
  width: 100%;
}
</style>

2.3 使用驗證碼組件

在需要使用驗證碼的頁面中:

<template>
  <div class="login-form">
    <el-form>
      <el-form-item label="手機號">
        <el-input v-model="mobile" placeholder="請輸入手機號" />
      </el-form-item>
      <el-form-item label="驗證碼">
        <el-input v-model="code" placeholder="請輸入驗證碼">
          <template #append>
            <SlideCaptcha 
              @success="handleCaptchaSuccess"
              btn-text="獲取驗證碼"
            />
          </template>
        </el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleLogin">登錄</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import SlideCaptcha from '@/components/SlideCaptcha.vue'
import { httpPost } from '@/utils/http'
const mobile = ref('')
const code = ref('')
// 驗證碼驗證成功后的回調(diào)
const handleCaptchaSuccess = () => {
  // 發(fā)送短信驗證碼
  httpPost('/api/sms/send', { mobile: mobile.value })
    .then(() => {
      ElMessage.success('驗證碼已發(fā)送')
    })
    .catch((e) => {
      ElMessage.error('發(fā)送失?。? + e.message)
    })
}
const handleLogin = () => {
  // 登錄邏輯
  console.log('登錄', mobile.value, code.value)
}
</script>

2.4 HTTP 工具函數(shù)

創(chuàng)建 utils/http.js 文件:

import axios from 'axios'
const http = axios.create({
  baseURL: 'http://localhost:8080',
  timeout: 10000
})
// 請求攔截器
http.interceptors.request.use(
  config => {
    // 可以在這里添加 token
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
// 響應攔截器
http.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code !== 0) {
      return Promise.reject(new Error(res.error || 'Error'))
    }
    return res
  },
  error => {
    return Promise.reject(error)
  }
)
export const httpPost = (url, data) => {
  return http.post(url, data)
}
export const httpGet = (url, params) => {
  return http.get(url, { params })
}

三、核心流程說明

3.1 驗證碼生成流程

  1. 前端點擊"獲取驗證碼"按鈕
  2. 前端調(diào)用 /api/captcha 接口
  3. 后端生成驗證碼圖片和答案坐標
  4. 后端將完整數(shù)據(jù)(包含答案)存儲到 Redis,有效期1分鐘
  5. 后端返回驗證碼ID和圖片數(shù)據(jù)(不包含答案)給前端
  6. 前端展示滑動驗證碼彈窗

3.2 驗證碼驗證流程

  1. 用戶拖動滑塊到目標位置
  2. 前端獲取滑塊坐標,調(diào)用 /api/captcha/verify 接口
  3. 后端從 Redis 獲取驗證碼答案數(shù)據(jù)
  4. 后端比對用戶滑動坐標與答案坐標(允許一定偏差)
  5. 驗證成功后刪除 Redis 數(shù)據(jù),返回成功響應
  6. 前端收到成功響應后執(zhí)行后續(xù)業(yè)務邏輯

3.3 安全性說明

  1. 答案不暴露:驗證碼答案坐標只存儲在服務端 Redis 中,不返回給前端
  2. 一次性使用:驗證成功后立即刪除 Redis 數(shù)據(jù),防止重復使用
  3. 時效性:驗證碼有效期1分鐘,過期自動失效
  4. 偏差容忍:允許用戶滑動有一定誤差(默認10像素),提升用戶體驗

四、常見問題

4.1 驗證碼圖片不顯示

檢查 base64 編碼是否正確,確保前端正確解析 data:image/png;base64, 前綴。

4.2 驗證總是失敗

檢查偏差值設置是否合理,可以適當增大 Deviation 常量的值。

4.3 Redis 連接失敗

確保 Redis 服務已啟動,檢查連接地址和端口是否正確。

4.4 跨域問題

在 Gin 中添加 CORS 中間件:

import "github.com/gin-contrib/cors"
r.Use(cors.Default())

五、總結

本文介紹了如何使用 go-captcha 庫在 Go + Vue 項目中實現(xiàn)滑動驗證碼功能。核心要點:

  1. 后端使用 go-captcha 生成驗證碼圖片和答案
  2. 使用 Redis 存儲驗證碼數(shù)據(jù),保證安全性和時效性
  3. 前端使用 go-captcha-vue 組件展示驗證碼
  4. 驗證流程簡單清晰,用戶體驗良好

完整代碼可以直接應用到新項目中,只需根據(jù)實際情況調(diào)整路由、響應格式等細節(jié)即可。

參考資源

到此這篇關于Go + Vue 實現(xiàn)行為驗證碼完整指南的文章就介紹到這了,更多相關go vue行為驗證碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Vue 使用依賴注入的方式共享數(shù)據(jù)的過程

    Vue 使用依賴注入的方式共享數(shù)據(jù)的過程

    賴注入的方式共享數(shù)據(jù)在Vue中是一種高級特性,它主要用于開發(fā)插件或庫,或者處理一些特殊的場景,這篇文章主要介紹了Vue 使用依賴注入的方式共享數(shù)據(jù),需要的朋友可以參考下
    2023-11-11
  • Vue中父子組件的值傳遞與方法傳遞

    Vue中父子組件的值傳遞與方法傳遞

    這篇文章主要介紹了Vue中父子組件的值傳遞與方法傳遞,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • vue-week-picker實現(xiàn)支持按周切換的日歷

    vue-week-picker實現(xiàn)支持按周切換的日歷

    這篇文章主要為大家詳細介紹了vue-week-picker實現(xiàn)支持按周切換的日歷,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • VueJS設計與實現(xiàn)之淺響應與深響應詳解

    VueJS設計與實現(xiàn)之淺響應與深響應詳解

    這篇文章主要為大家介紹了VueJS設計與實現(xiàn)之淺響應與深響應詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Element?plus?表單中下拉框的空值問題解決

    Element?plus?表單中下拉框的空值問題解決

    有時候會碰到查詢條件需要用下拉框來區(qū)分的時候,比如需要區(qū)分全部?|?啟用?|?停用三個狀態(tài),這時一般會給全部的value值設置為'',但是這樣會導致下拉框無法選中對應的全部選項,本文就來介紹一下這個問題解決,感興趣的可以了解一下
    2024-11-11
  • Vuex state中同步數(shù)據(jù)和異步數(shù)據(jù)方式

    Vuex state中同步數(shù)據(jù)和異步數(shù)據(jù)方式

    這篇文章主要介紹了Vuex state中同步數(shù)據(jù)和異步數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Vue3將虛擬節(jié)點渲染到網(wǎng)頁初次渲染詳解

    Vue3將虛擬節(jié)點渲染到網(wǎng)頁初次渲染詳解

    這篇文章主要為大家介紹了Vue3將虛擬節(jié)點渲染到網(wǎng)頁初次渲染詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • vue中巧用三元表達式解析

    vue中巧用三元表達式解析

    這篇文章主要介紹了vue中巧用三元表達式解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue2實現(xiàn)未登錄攔截頁面功能的基本步驟和示例代碼

    Vue2實現(xiàn)未登錄攔截頁面功能的基本步驟和示例代碼

    在Vue 2中實現(xiàn)未登錄攔截頁面功能,通??梢酝ㄟ^路由守衛(wèi)和全局前置守衛(wèi)來完成,以下是一個基本的實現(xiàn)步驟和示例代碼,幫助你創(chuàng)建一個簡單的未登錄攔截邏輯,需要的朋友可以參考下
    2024-04-04
  • vue-router history模式下的微信分享小結

    vue-router history模式下的微信分享小結

    本篇文章主要介紹了vue-router history模式下的微信分享小結,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07

最新評論

泸水县| 兴化市| 潢川县| 双流县| 浪卡子县| 武义县| 丹东市| 乌海市| 荃湾区| 桑日县| 德阳市| 贺州市| 宣恩县| 长治县| 木里| 建水县| 旺苍县| 抚松县| 子洲县| 孝感市| 威宁| 南阳市| 苗栗市| 福州市| 沧州市| 江津市| 临江市| 陆川县| 中西区| 永嘉县| 安龙县| 淮安市| 荔浦县| 双辽市| 连江县| 岑巩县| 叶城县| 介休市| 安仁县| 富源县| 玛纳斯县|