之前在项目中,一直使用VUE前端的图片验证码。前几天看到文章,说前端验证容易被绕过,那就准备使用后端验证吧。
用golang就选择github.com/dchest/captcha了,折腾一番后,要点记录如下:
控制器:
package controllers
import (
"bytes"
"log"
"net/http"
"strconv"
"strings"
"time"
"utils"
"github.com/dchest/captcha"
"github.com/gin-gonic/gin"
)
type CheckCodeController struct {
}
/***
*后端图形码验证
*/
func (serviceCheckCode *CheckCodeController) VerifyCode(c *gin.Context) {
captchaId := c.Query("captchaId")
pngCode := c.Query("pngCode")
if captcha.VerifyString(captchaId, pngCode) {
result := utils.ResultSuccess() //自己写的用于规范一般状态码传递的package
c.JSON(http.StatusOK, gin.H{
"data": result,
})
} else {
result := utils.ResultFailue()
result.Msg = "图形验证码错误"
c.JSON(http.StatusOK, gin.H{
"data": result,
})
}
}
/***
刷新验证码,也作为初次获取验证码使用
*/
func (serviceCheckCode *CheckCodeController) ReloadVerifyCode(c *gin.Context) {
id := captcha.NewLen(4) //一般用4位数字
result := utils.ResultSuccess()
result.Msg = id //偷懒一下,直接用这个来传递captchaId到前端
c.JSON(http.StatusOK, gin.H{
"data": result,
})
}
/**
显示图形验证码
因为要传递图片,所以要对Header做一些设置
*/
func (serviceCheckCode *CheckCodeController) GenVerifyCode(c *gin.Context) {
c.Writer.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
c.Writer.Header().Set("Pragma", "no-cache")
c.Writer.Header().Set("Expires", "0")
c.Writer.Header().Set("Content-Type", "image/png")
id := c.Param("captchaId")
id = strings.Replace(id, "/", "", 1)
var content bytes.Buffer
captcha.WriteImage(&content, id, 100, 50) //4位验证码,宽100,高50最清晰
http.ServeContent(c.Writer, c.Request, id+".png", time.Time{}, bytes.NewReader(content.Bytes()))
return
}
路由:
checkCodeRoute := router.Group("/check-code")
{
checkCodeController := new(controllers.CheckCodeController)
//刷新图形验证码
checkCodeRoute.GET("refresh.html", checkCodeController.ReloadVerifyCode)
//获取图形验证码
checkCodeRoute.GET("/show/*captchaId", checkCodeController.GenVerifyCode)
//验证图形码
checkCodeRoute.GET("/verify", checkCodeController.VerifyCode)
}
前端:
<template>
<div>
<div>
<el-input v-model="pngCode" placeholder="请输入图片验证码" style="width: 120px;"></el-input>
<span @click="getCaptchaImgUrl" class="box-verify"><img id="captchaIdImg" :src="captchaImgUrl" /></span>
<el-button @click="verifyCaptcha">验证</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
captchaId: '',
captchaImgUrl: '',
pngCode: ''
}
},
methods: {
getCaptchaImgUrl() {
this.axios.get('/check-code/refresh.html', {})
.then(res => {
this.captchaId = res.data.data.Msg
let url = window.location.href
let host = window.location.host
let n = host.indexOf(":", 0)
let k = url.indexOf(host.slice(0,n), 0)
//这里的端口8000是后端gin的端口
this.captchaImgUrl = url.slice(0,k) + host.slice(0,n) + ':8000/web/check-code/show/' + res.data.data.Msg
}).catch(err => {
console.log(err)
})
// console.log(this.captchaImgUrl)
},
verifyCaptcha() {
// 因为显示图片验证码的时候, header被改成了'Content-Type': 'image/png',所以
this.axios({
methods:"GET",
url:'/check-code/verify',
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
},
params: {
"captchaId": this.captchaId,
"pngCode": this.pngCode
}
}).then(res => {
if (res.data.data.Code == 200 ) {
console.log(res.data.data.Msg)
} else {
console.log(res.data.data.Msg)
}
}).catch(err => {
console.log(err)
})
}
},
mounted() {
this.getCaptchaImgUrl()
}
}
</script>
<!-- 样式 -->
<style>
.box-verify img{
vertical-align: middle;
}
</style>
大功告成!