mirror of
https://github.com/kongyuebin1/dongfeng-pay.git
synced 2024-11-14 14:39:21 +08:00
441 lines
8.9 KiB
Go
441 lines
8.9 KiB
Go
/***************************************************
|
|
** @Desc : generate login verify code image
|
|
** @Time : 2019/8/7 17:14
|
|
** @Author : yuebin
|
|
** @File : login_verify_code
|
|
** @Last Modified by : yuebin
|
|
** @Last Modified time: 2019/8/7 17:14
|
|
** @Software: GoLand
|
|
****************************************************/
|
|
package utils
|
|
|
|
import (
|
|
crand "crypto/rand"
|
|
"image"
|
|
"image/color"
|
|
"math/rand"
|
|
"time"
|
|
"io"
|
|
"image/png"
|
|
"net/http"
|
|
"strconv"
|
|
"fmt"
|
|
)
|
|
|
|
const (
|
|
stdWidth = 100
|
|
stdHeight = 40
|
|
maxSkew = 2
|
|
)
|
|
|
|
const (
|
|
fontWidth = 5
|
|
fontHeight = 8
|
|
blackChar = 1
|
|
)
|
|
|
|
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(int64(time.Second))
|
|
|
|
}
|
|
|
|
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(0, 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(3, 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
|
|
|
|
}
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
const (
|
|
// Standard length of uniuri string to achive ~95 bits of entropy.
|
|
StdLen = 16
|
|
// Length of uniurl string to achive ~119 bits of entropy, closest
|
|
// to what can be losslessly converted to UUIDv4 (122 bits).
|
|
UUIDLen = 20
|
|
)
|
|
|
|
// Standard characters allowed in uniuri string.
|
|
|
|
var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
|
|
|
// New returns a new random string of the standard length, consisting of
|
|
// standard characters.
|
|
|
|
func New() string {
|
|
return NewLenChars(StdLen, StdChars)
|
|
|
|
}
|
|
|
|
// NewLen returns a new random string of the provided length, consisting of
|
|
// standard characters.
|
|
|
|
func NewLen(length int) string {
|
|
return NewLenChars(length, StdChars)
|
|
|
|
}
|
|
|
|
// NewLenChars returns a new random string of the provided length, consisting
|
|
// of the provided byte slice of allowed characters (maximum 256).
|
|
|
|
func NewLenChars(length int, chars []byte) string {
|
|
b := make([]byte, length)
|
|
r := make([]byte, length+(length/4)) // storage for random bytes.
|
|
clen := byte(len(chars))
|
|
maxrb := byte(256 - (256 % len(chars)))
|
|
i := 0
|
|
for {
|
|
if _, err := io.ReadFull(crand.Reader, r); err != nil {
|
|
panic("error reading from random source: " + err.Error())
|
|
}
|
|
for _, c := range r {
|
|
if c >= maxrb {
|
|
// Skip this number to avoid modulo bias.
|
|
continue
|
|
}
|
|
b[i] = chars[c%clen]
|
|
i++
|
|
if i == length {
|
|
return string(b)
|
|
}
|
|
}
|
|
}
|
|
panic("unreachable")
|
|
|
|
}
|
|
|
|
func GenerateVerifyCodeImg() (*Image, string) {
|
|
d := make([]byte, 4)
|
|
s := NewLen(4)
|
|
ss := ""
|
|
d = []byte(s)
|
|
for v := range d {
|
|
d[v] %= 10
|
|
ss += strconv.FormatInt(int64(d[v]), 32)
|
|
}
|
|
return NewImage(d, 100, 40), ss
|
|
}
|
|
|
|
func pic(w http.ResponseWriter, req *http.Request) {
|
|
d := make([]byte, 4)
|
|
s := NewLen(4)
|
|
ss := ""
|
|
d = []byte(s)
|
|
for v := range d {
|
|
d[v] %= 10
|
|
ss += strconv.FormatInt(int64(d[v]), 32)
|
|
}
|
|
w.Header().Set("Content-Type", "image/png")
|
|
NewImage(d, 100, 40).WriteTo(w)
|
|
fmt.Println(ss)
|
|
|
|
}
|
|
|
|
func index(w http.ResponseWriter, req *http.Request) {
|
|
str := "<meta charset=\"utf-8\"><h3>golang 图片验证码例子</h3><img border=\"1\" src=\"/pic\" alt=\"图片验证码\" onclick=\"this.src='/pic'\" />"
|
|
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()
|
|
|
|
}
|