导航

Go 语言做的几个验证码

Posted on 2013-10-28 17:33  蝈蝈俊  阅读(4360)  评论(0编辑  收藏  举报

1、http://www.oschina.net/code/snippet_173630_12006

效果:

image image

image image image

源代码:

   1: package main
   2:  
   3: import (
   4:     crand "crypto/rand"
   5:     "fmt"
   6:     "image"
   7:     "image/color"
   8:     "image/png"
   9:     "io"
  10:     "math/rand"
  11:     "net/http"
  12:     "strconv"
  13:     "time"
  14: )
  15:  
  16: const (
  17:     stdWidth  = 100
  18:     stdHeight = 40
  19:     maxSkew   = 2
  20: )
  21:  
  22: const (
  23:     fontWidth  = 5
  24:     fontHeight = 8
  25:     blackChar  = 1
  26: )
  27:  
  28: var font = [][]byte{
  29:     { // 0
  30:         0, 1, 1, 1, 0,
  31:         1, 0, 0, 0, 1,
  32:         1, 0, 0, 0, 1,
  33:         1, 0, 0, 0, 1,
  34:         1, 0, 0, 0, 1,
  35:         1, 0, 0, 0, 1,
  36:         1, 0, 0, 0, 1,
  37:         0, 1, 1, 1, 0},
  38:     { // 1
  39:         0, 0, 1, 0, 0,
  40:         0, 1, 1, 0, 0,
  41:         1, 0, 1, 0, 0,
  42:         0, 0, 1, 0, 0,
  43:         0, 0, 1, 0, 0,
  44:         0, 0, 1, 0, 0,
  45:         0, 0, 1, 0, 0,
  46:         1, 1, 1, 1, 1},
  47:     { // 2
  48:         0, 1, 1, 1, 0,
  49:         1, 0, 0, 0, 1,
  50:         0, 0, 0, 0, 1,
  51:         0, 0, 0, 1, 1,
  52:         0, 1, 1, 0, 0,
  53:         1, 0, 0, 0, 0,
  54:         1, 0, 0, 0, 0,
  55:         1, 1, 1, 1, 1},
  56:     { // 3
  57:         1, 1, 1, 1, 0,
  58:         0, 0, 0, 0, 1,
  59:         0, 0, 0, 1, 0,
  60:         0, 1, 1, 1, 0,
  61:         0, 0, 0, 1, 0,
  62:         0, 0, 0, 0, 1,
  63:         0, 0, 0, 0, 1,
  64:         1, 1, 1, 1, 0},
  65:     { // 4
  66:         1, 0, 0, 1, 0,
  67:         1, 0, 0, 1, 0,
  68:         1, 0, 0, 1, 0,
  69:         1, 0, 0, 1, 0,
  70:         1, 1, 1, 1, 1,
  71:         0, 0, 0, 1, 0,
  72:         0, 0, 0, 1, 0,
  73:         0, 0, 0, 1, 0},
  74:     { // 5
  75:         1, 1, 1, 1, 1,
  76:         1, 0, 0, 0, 0,
  77:         1, 0, 0, 0, 0,
  78:         1, 1, 1, 1, 0,
  79:         0, 0, 0, 0, 1,
  80:         0, 0, 0, 0, 1,
  81:         0, 0, 0, 0, 1,
  82:         1, 1, 1, 1, 0},
  83:     { // 6
  84:         0, 0, 1, 1, 1,
  85:         0, 1, 0, 0, 0,
  86:         1, 0, 0, 0, 0,
  87:         1, 1, 1, 1, 0,
  88:         1, 0, 0, 0, 1,
  89:         1, 0, 0, 0, 1,
  90:         1, 0, 0, 0, 1,
  91:         0, 1, 1, 1, 0},
  92:     { // 7
  93:         1, 1, 1, 1, 1,
  94:         0, 0, 0, 0, 1,
  95:         0, 0, 0, 0, 1,
  96:         0, 0, 0, 1, 0,
  97:         0, 0, 1, 0, 0,
  98:         0, 1, 0, 0, 0,
  99:         0, 1, 0, 0, 0,
 100:         0, 1, 0, 0, 0},
 101:     { // 8
 102:         0, 1, 1, 1, 0,
 103:         1, 0, 0, 0, 1,
 104:         1, 0, 0, 0, 1,
 105:         0, 1, 1, 1, 0,
 106:         1, 0, 0, 0, 1,
 107:         1, 0, 0, 0, 1,
 108:         1, 0, 0, 0, 1,
 109:         0, 1, 1, 1, 0},
 110:     { // 9
 111:         0, 1, 1, 1, 0,
 112:         1, 0, 0, 0, 1,
 113:         1, 0, 0, 0, 1,
 114:         1, 1, 0, 0, 1,
 115:         0, 1, 1, 1, 1,
 116:         0, 0, 0, 0, 1,
 117:         0, 0, 0, 0, 1,
 118:         1, 1, 1, 1, 0},
 119: }
 120:  
 121: type Image struct {
 122:     *image.NRGBA
 123:     color   *color.NRGBA
 124:     width   int //a digit width
 125:     height  int //a digit height
 126:     dotsize int
 127: }
 128:  
 129: func init() {
 130:     rand.Seed(int64(time.Second))
 131:  
 132: }
 133:  
 134: func NewImage(digits []byte, width, height int) *Image {
 135:     img := new(Image)
 136:     r := image.Rect(img.width, img.height, stdWidth, stdHeight)
 137:     img.NRGBA = image.NewNRGBA(r)
 138:     img.color = &color.NRGBA{
 139:         uint8(rand.Intn(129)),
 140:         uint8(rand.Intn(129)),
 141:         uint8(rand.Intn(129)),
 142:         0xFF}
 143:     // Draw background (10 random circles of random brightness)
 144:     img.calculateSizes(width, height, len(digits))
 145:     img.fillWithCircles(10, img.dotsize)
 146:     maxx := width - (img.width+img.dotsize)*len(digits) - img.dotsize
 147:     maxy := height - img.height - img.dotsize*2
 148:     x := rnd(img.dotsize*2, maxx)
 149:     y := rnd(img.dotsize*2, maxy)
 150:     // Draw digits.
 151:     for _, n := range digits {
 152:         img.drawDigit(font[n], x, y)
 153:         x += img.width + img.dotsize
 154:     }
 155:     // Draw strike-through line.
 156:     img.strikeThrough()
 157:     return img
 158:  
 159: }
 160:  
 161: func (img *Image) WriteTo(w io.Writer) (int64, error) {
 162:     return 0, png.Encode(w, img)
 163:  
 164: }
 165:  
 166: func (img *Image) calculateSizes(width, height, ncount int) {
 167:     // Goal: fit all digits inside the image.
 168:     var border int
 169:     if width > height {
 170:         border = height / 5
 171:     } else {
 172:         border = width / 5
 173:     }
 174:     // Convert everything to floats for calculations.
 175:     w := float64(width - border*2)  //268
 176:     h := float64(height - border*2) //48
 177:     // fw takes into account 1-dot spacing between digits.
 178:     fw := float64(fontWidth) + 1 //6
 179:     fh := float64(fontHeight)    //8
 180:     nc := float64(ncount)        //7
 181:     // Calculate the width of a single digit taking into account only the
 182:     // width of the image.
 183:     nw := w / nc //38
 184:     // Calculate the height of a digit from this width.
 185:     nh := nw * fh / fw //51
 186:     // Digit too high?
 187:     if nh > h {
 188:         // Fit digits based on height.
 189:         nh = h //nh = 44
 190:         nw = fw / fh * nh
 191:     }
 192:     // Calculate dot size.
 193:     img.dotsize = int(nh / fh)
 194:     // Save everything, making the actual width smaller by 1 dot to account
 195:     // for spacing between digits.
 196:     img.width = int(nw)
 197:     img.height = int(nh) - img.dotsize
 198:  
 199: }
 200:  
 201: func (img *Image) fillWithCircles(n, maxradius int) {
 202:     color := img.color
 203:     maxx := img.Bounds().Max.X
 204:     maxy := img.Bounds().Max.Y
 205:     for i := 0; i < n; i++ {
 206:         setRandomBrightness(color, 255)
 207:         r := rnd(1, maxradius)
 208:         img.drawCircle(color, rnd(r, maxx-r), rnd(r, maxy-r), r)
 209:     }
 210:  
 211: }
 212:  
 213: func (img *Image) drawHorizLine(color color.Color, fromX, toX, y int) {
 214:     for x := fromX; x <= toX; x++ {
 215:         img.Set(x, y, color)
 216:     }
 217:  
 218: }
 219:  
 220: func (img *Image) drawCircle(color color.Color, x, y, radius int) {
 221:     f := 1 - radius
 222:     dfx := 1
 223:     dfy := -2 * radius
 224:     xx := 0
 225:     yy := radius
 226:     img.Set(x, y+radius, color)
 227:     img.Set(x, y-radius, color)
 228:     img.drawHorizLine(color, x-radius, x+radius, y)
 229:     for xx < yy {
 230:         if f >= 0 {
 231:             yy--
 232:             dfy += 2
 233:             f += dfy
 234:         }
 235:         xx++
 236:         dfx += 2
 237:         f += dfx
 238:         img.drawHorizLine(color, x-xx, x+xx, y+yy)
 239:         img.drawHorizLine(color, x-xx, x+xx, y-yy)
 240:         img.drawHorizLine(color, x-yy, x+yy, y+xx)
 241:         img.drawHorizLine(color, x-yy, x+yy, y-xx)
 242:     }
 243:  
 244: }
 245:  
 246: func (img *Image) strikeThrough() {
 247:     r := 0
 248:     maxx := img.Bounds().Max.X
 249:     maxy := img.Bounds().Max.Y
 250:     y := rnd(maxy/3, maxy-maxy/3)
 251:     for x := 0; x < maxx; x += r {
 252:         r = rnd(1, img.dotsize/3)
 253:         y += rnd(-img.dotsize/2, img.dotsize/2)
 254:         if y <= 0 || y >= maxy {
 255:             y = rnd(maxy/3, maxy-maxy/3)
 256:         }
 257:         img.drawCircle(img.color, x, y, r)
 258:     }
 259:  
 260: }
 261:  
 262: func (img *Image) drawDigit(digit []byte, x, y int) {
 263:     skf := rand.Float64() * float64(rnd(-maxSkew, maxSkew))
 264:     xs := float64(x)
 265:     minr := img.dotsize / 2               // minumum radius
 266:     maxr := img.dotsize/2 + img.dotsize/4 // maximum radius
 267:     y += rnd(-minr, minr)
 268:     for yy := 0; yy < fontHeight; yy++ {
 269:         for xx := 0; xx < fontWidth; xx++ {
 270:             if digit[yy*fontWidth+xx] != blackChar {
 271:                 continue
 272:             }
 273:             // Introduce random variations.
 274:             or := rnd(minr, maxr)
 275:             ox := x + (xx * img.dotsize) + rnd(0, or/2)
 276:             oy := y + (yy * img.dotsize) + rnd(0, or/2)
 277:             img.drawCircle(img.color, ox, oy, or)
 278:         }
 279:         xs += skf
 280:         x = int(xs)
 281:     }
 282:  
 283: }
 284:  
 285: func setRandomBrightness(c *color.NRGBA, max uint8) {
 286:     minc := min3(c.R, c.G, c.B)
 287:     maxc := max3(c.R, c.G, c.B)
 288:     if maxc > max {
 289:         return
 290:     }
 291:     n := rand.Intn(int(max-maxc)) - int(minc)
 292:     c.R = uint8(int(c.R) + n)
 293:     c.G = uint8(int(c.G) + n)
 294:     c.B = uint8(int(c.B) + n)
 295:  
 296: }
 297:  
 298: func min3(x, y, z uint8) (o uint8) {
 299:     o = x
 300:     if y < o {
 301:         o = y
 302:     }
 303:     if z < o {
 304:         o = z
 305:     }
 306:     return
 307:  
 308: }
 309:  
 310: func max3(x, y, z uint8) (o uint8) {
 311:     o = x
 312:     if y > o {
 313:         o = y
 314:     }
 315:     if z > o {
 316:         o = z
 317:     }
 318:     return
 319:  
 320: }
 321:  
 322: // rnd returns a random number in range [from, to].
 323:  
 324: func rnd(from, to int) int {
 325:     //println(to+1-from)
 326:     return rand.Intn(to+1-from) + from
 327:  
 328: }
 329:  
 330: const (
 331:     // Standard length of uniuri string to achive ~95 bits of entropy.
 332:     StdLen = 16
 333:     // Length of uniurl string to achive ~119 bits of entropy, closest
 334:     // to what can be losslessly converted to UUIDv4 (122 bits).
 335:     UUIDLen = 20
 336: )
 337:  
 338: // Standard characters allowed in uniuri string.
 339:  
 340: var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
 341:  
 342: // New returns a new random string of the standard length, consisting of
 343: // standard characters.
 344:  
 345: func New() string {
 346:     return NewLenChars(StdLen, StdChars)
 347:  
 348: }
 349:  
 350: // NewLen returns a new random string of the provided length, consisting of
 351: // standard characters.
 352:  
 353: func NewLen(length int) string {
 354:     return NewLenChars(length, StdChars)
 355:  
 356: }
 357:  
 358: // NewLenChars returns a new random string of the provided length, consisting
 359: // of the provided byte slice of allowed characters (maximum 256).
 360:  
 361: func NewLenChars(length int, chars []byte) string {
 362:     b := make([]byte, length)
 363:     r := make([]byte, length+(length/4)) // storage for random bytes.
 364:     clen := byte(len(chars))
 365:     maxrb := byte(256 - (256 % len(chars)))
 366:     i := 0
 367:     for {
 368:         if _, err := io.ReadFull(crand.Reader, r); err != nil {
 369:             panic("error reading from random source: " + err.Error())
 370:         }
 371:         for _, c := range r {
 372:             if c >= maxrb {
 373:                 // Skip this number to avoid modulo bias.
 374:                 continue
 375:             }
 376:             b[i] = chars[c%clen]
 377:             i++
 378:             if i == length {
 379:                 return string(b)
 380:             }
 381:         }
 382:     }
 383:     panic("unreachable")
 384:  
 385: }
 386:  
 387: func pic(w http.ResponseWriter, req *http.Request) {
 388:     d := make([]byte, 4)
 389:     s := NewLen(4)
 390:     ss := ""
 391:     d = []byte(s)
 392:     for v := range d {
 393:         d[v] %= 10
 394:         ss += strconv.FormatInt(int64(d[v]), 32)
 395:     }
 396:     w.Header().Set("Content-Type", "image/png")
 397:     NewImage(d, 100, 40).WriteTo(w)
 398:     fmt.Println(ss)
 399:  
 400: }
 401:  
 402: func index(w http.ResponseWriter, req *http.Request) {
 403:     str := "<meta charset=\"utf-8\"><h3>golang 图片验证码例子</h3><img border=\"1\" src=\"/pic\" alt=\"图片验证码\" onclick=\"this.src='/pic'\" />"
 404:     w.Header().Set("Content-Type", "text/html")
 405:     w.Write([]byte(str))
 406:  
 407: }
 408:  
 409: func main() {
 410:     http.HandleFunc("/pic", pic)
 411:     http.HandleFunc("/", index)
 412:     s := &http.Server{
 413:         Addr:           ":8080",
 414:         ReadTimeout:    30 * time.Second,
 415:         WriteTimeout:   30 * time.Second,
 416:         MaxHeaderBytes: 1 << 20}
 417:     s.ListenAndServe()
 418:  
 419: }

 

2、https://github.com/dchest/captcha 

它比较强大,可以生成图片的验证码或者音频验证码。

image image

image image image