go 网上简单验证码的demo

golang cyanprobe 7年前 (2017-09-03) 5164次浏览 已收录 2个评论

前言:

搭go学习web架子的时候看需要验证码看了网上一种实现。

正文:

其实就是绘制点, 网上的demo都有一个字符对应的过程生成256标准的字符对应表喵??搞了半天也不知道为什么这么做,我还以为要增加随机性(防止机器人模拟随机??)网上问了很多人,也看了所谓的这段代码的注释详解。最后我特么才发现 ——他这复制的uniuri的源码,其实根本无卵用。我靠,那些强行解释的难道没发现。。,然后直接替换成随机数字拼接在一起就好了。个人以为他要拼接出uuid类似的东西是为了用字符短暂标识数字验证码但是后面又转回去了根本没什么意义不知道在搞毛。 (难道还是我太弱智?感觉最初的也不是原创……)
然后那个方法的

io.ReadFull(crand.Reader, r)

现在的的crand.read()内部已经使用了io.ReadFull  方法,所以可以不用这样写来读取字节缓存了,关于io.ReadFull :
读取正好len(buf)长度的字节。如果字节数不是指定长度,则返回错误信息和正确的字节数。当没有字节能被读时,返回EOF错误。如果读了一些,但是没读完产生EOF错误时,返回ErrUnexpectedEOF错误。
所以不需要那样写了,看到uniuri已经直接使用crand.read方法了。
实现就是绘制点,绘制干扰线(偏移),干扰点

package main
import (
	"fmt"
	"image"
	"image/color"
	"image/png"
	"io"
	"math/rand"
	"net/http"
	"time"
)
const (
	stdWidth  = 100
	stdHeight = 40
	maxSkew   = 2
)
const (
	fontWidth  = 5
	fontHeight = 8
	blackChar  = 1
)
var captcha = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var font = [][]byte{
	{ // 0
		0, 1, 1, 1, 0,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		0, 1, 1, 1, 0,
	},
	{ // 1
		0, 0, 1, 0, 0,
		0, 1, 1, 0, 0,
		1, 0, 1, 0, 0,
		0, 0, 1, 0, 0,
		0, 0, 1, 0, 0,
		0, 0, 1, 0, 0,
		0, 0, 1, 0, 0,
		1, 1, 1, 1, 1,
	},
	{ // 2
		0, 1, 1, 1, 0,
		1, 0, 0, 0, 1,
		0, 0, 0, 0, 1,
		0, 0, 0, 1, 1,
		0, 1, 1, 0, 0,
		1, 0, 0, 0, 0,
		1, 0, 0, 0, 0,
		1, 1, 1, 1, 1,
	},
	{ // 3
		1, 1, 1, 1, 0,
		0, 0, 0, 0, 1,
		0, 0, 0, 1, 0,
		0, 1, 1, 1, 0,
		0, 0, 0, 1, 0,
		0, 0, 0, 0, 1,
		0, 0, 0, 0, 1,
		1, 1, 1, 1, 0,
	},
	{ // 4
		1, 0, 0, 1, 0,
		1, 0, 0, 1, 0,
		1, 0, 0, 1, 0,
		1, 0, 0, 1, 0,
		1, 1, 1, 1, 1,
		0, 0, 0, 1, 0,
		0, 0, 0, 1, 0,
		0, 0, 0, 1, 0,
	},
	{ // 5
		1, 1, 1, 1, 1,
		1, 0, 0, 0, 0,
		1, 0, 0, 0, 0,
		1, 1, 1, 1, 0,
		0, 0, 0, 0, 1,
		0, 0, 0, 0, 1,
		0, 0, 0, 0, 1,
		1, 1, 1, 1, 0,
	},
	{ // 6
		0, 0, 1, 1, 1,
		0, 1, 0, 0, 0,
		1, 0, 0, 0, 0,
		1, 1, 1, 1, 0,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		0, 1, 1, 1, 0,
	},
	{ // 7
		1, 1, 1, 1, 1,
		0, 0, 0, 0, 1,
		0, 0, 0, 0, 1,
		0, 0, 0, 1, 0,
		0, 0, 1, 0, 0,
		0, 1, 0, 0, 0,
		0, 1, 0, 0, 0,
		0, 1, 0, 0, 0,
	},
	{ // 8
		0, 1, 1, 1, 0,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		0, 1, 1, 1, 0,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		0, 1, 1, 1, 0,
	},
	{ // 9
		0, 1, 1, 1, 0,
		1, 0, 0, 0, 1,
		1, 0, 0, 0, 1,
		1, 1, 0, 0, 1,
		0, 1, 1, 1, 1,
		0, 0, 0, 0, 1,
		0, 0, 0, 0, 1,
		1, 1, 1, 1, 0,
	},
}
type Image struct {
	*image.NRGBA
	color   *color.NRGBA
	width   int //a digit width
	height  int //a digit height
	dotsize int
}
func init() {
	rand.Seed(time.Now().UnixNano())
}
func NewImage(digits []byte, width, height int) *Image {
	img := new(Image)
	r := image.Rect(img.width, img.height, stdWidth, stdHeight)
	img.NRGBA = image.NewNRGBA(r)
	img.color = &color.NRGBA{
		uint8(rand.Intn(129)),
		uint8(rand.Intn(129)),
		uint8(rand.Intn(129)),
		0xFF,
	}
	// Draw background (10 random circles of random brightness)
	img.calculateSizes(width, height, len(digits))
	// img.fillWithCircles(10, img.dotsize)
	maxx := width - (img.width+img.dotsize)*len(digits) - img.dotsize
	maxy := height - img.height - img.dotsize*2
	x := rnd(img.dotsize*2, maxx)
	y := rnd(img.dotsize*2, maxy)
	// Draw digits.
	for _, n := range digits {
		img.drawDigit(font[n], x, y)
		x += img.width + img.dotsize
	}
	// Draw strike-through line.
	// img.strikeThrough()
	return img
}
func (img *Image) WriteTo(w io.Writer) (int64, error) {
	return 0, png.Encode(w, img)
}
func (img *Image) calculateSizes(width, height, ncount int) {
	// Goal: fit all digits inside the image.
	var border int
	if width > height {
		border = height / 5
	} else {
		border = width / 5
	}
	//保证绘制位置在内部
	// Convert everything to floats for calculations.
	w := float64(width - border*2)  //268
	h := float64(height - border*2) //48
	// fw takes into account 1-dot spacing between digits.
	//间隔
	fw := float64(fontWidth) + 1 //6
	fh := float64(fontHeight) //8
	nc := float64(ncount)     //7
	// Calculate the width of a single digit taking into account only the
	// width of the image.
	nw := w / nc //38
	// Calculate the height of a digit from this width.
	nh := nw * fh / fw //51
	// Digit too high?
	if nh > h {
		// Fit digits based on height.
		nh = h //nh = 44
		nw = fw / fh * nh
	}
	// Calculate dot size.
	img.dotsize = int(nh / fh)
	// Save everything, making the actual width smaller by 1 dot to account
	// for spacing between digits.
	img.width = int(nw)
	img.height = int(nh) - img.dotsize
}
func (img *Image) fillWithCircles(n, maxradius int) {
	color := img.color
	maxx := img.Bounds().Max.X
	maxy := img.Bounds().Max.Y
	for i := 0; i < n; i++ {
		setRandomBrightness(color, 255)
		r := rnd(1, maxradius)
		img.drawCircle(color, rnd(r, maxx-r), rnd(r, maxy-r), r)
	}
}
func (img *Image) drawHorizLine(color color.Color, fromX, toX, y int) {
	for x := fromX; x <= toX; x++ {
		img.Set(x, y, color)
	}
}
func (img *Image) drawCircle(color color.Color, x, y, radius int) {
	f := 1 - radius
	dfx := 1
	dfy := -2 * radius
	xx := 0
	yy := radius
	img.Set(x, y+radius, color)
	img.Set(x, y-radius, color)
	img.drawHorizLine(color, x-radius, x+radius, y)
	for xx < yy { if f >= 0 {
			yy--
			dfy += 2
			f += dfy
		}
		xx++
		dfx += 2
		f += dfx
		img.drawHorizLine(color, x-xx, x+xx, y+yy)
		img.drawHorizLine(color, x-xx, x+xx, y-yy)
		img.drawHorizLine(color, x-yy, x+yy, y+xx)
		img.drawHorizLine(color, x-yy, x+yy, y-xx)
	}
}
func (img *Image) strikeThrough() {
	r := 0
	maxx := img.Bounds().Max.X
	maxy := img.Bounds().Max.Y
	y := rnd(maxy/3, maxy-maxy/3)
	for x := 0; x < maxx; x += r {
		r = rnd(1, img.dotsize/3)
		y += rnd(-img.dotsize/2, img.dotsize/2)
		if y <= 0 || y >= maxy {
			y = rnd(maxy/3, maxy-maxy/3)
		}
		img.drawCircle(img.color, x, y, r)
	}
}
func (img *Image) drawDigit(digit []byte, x, y int) {
	skf := rand.Float64() * float64(rnd(-maxSkew, maxSkew))
	xs := float64(x)
	minr := img.dotsize / 2               // minumum radius
	maxr := img.dotsize/2 + img.dotsize/4 // maximum radius
	y += rnd(-minr, minr)
	for yy := 0; yy < fontHeight; yy++ {
		for xx := 0; xx < fontWidth; xx++ { if digit[yy*fontWidth+xx] != blackChar { continue } // Introduce random variations. or := rnd(minr, maxr) ox := x + (xx * img.dotsize) + rnd(0, or/2) oy := y + (yy * img.dotsize) + rnd(0, or/2) img.drawCircle(img.color, ox, oy, or) } xs += skf x = int(xs) } } func setRandomBrightness(c *color.NRGBA, max uint8) { minc := min3(c.R, c.G, c.B) maxc := max3(c.R, c.G, c.B) //超过边界退出 if maxc > max {
		return
	}
	n := rand.Intn(int(max-maxc)) - int(minc)
	c.R = uint8(int(c.R) + n)
	c.G = uint8(int(c.G) + n)
	c.B = uint8(int(c.B) + n)
}
func min3(x, y, z uint8) (o uint8) {
	o = x
	if y < o {
		o = y
	}
	if z < o { o = z } return } func max3(x, y, z uint8) (o uint8) { o = x if y > o {
		o = y
	}
	if z > o {
		o = z
	}
	return
}
func getLen(i int) ([]byte, error) {
	rand.Seed(time.Now().UnixNano())
	var capCode []byte
	for n := 0; n < i; n++ {
		capCode = append(capCode, byte(rand.Intn(len(captcha)-1)))
	}
	return capCode, nil
}
// rnd returns a random number in range [from, to].
func rnd(from, to int) int {
	//println(to+1-from)
	return rand.Intn(to+1-from) + from
}
func pic(w http.ResponseWriter, req *http.Request) {
	d, _ := getLen(4)
	w.Header().Set("Content-Type", "image/png")
	NewImage(d, 100, 40).WriteTo(w)
	fmt.Print(d)
}
func index(w http.ResponseWriter, req *http.Request) {
	str :="<img src="\"/pic\"" alt="\"图片验证码\"" border="\"1\"" />"
	w.Header().Set("Content-Type", "text/html")
	w.Write([]byte(str))
}
func main() {
	http.HandleFunc("/pic", pic)
	http.HandleFunc("/", index)
	s := &http.Server{
		Addr:           ":8080",
		ReadTimeout:    30 * time.Second,
		WriteTimeout:   30 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	s.ListenAndServe()
}

CyanProbe , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:go 网上简单验证码的demo
喜欢 (1)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(2)个小伙伴在吐槽
  1. 感觉强行解释的很尴尬啊
    姜辰2017-09-03 17:22 回复
  2. 验证demo就是一串随机字符。
    夏日博客2017-09-12 14:12 回复