github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/captcha/captcha.go (about) 1 package captcha 2 3 import ( 4 "bytes" 5 crand "crypto/rand" 6 "image" 7 "image/color" 8 "image/png" 9 "io" 10 "math" 11 "math/rand" 12 "time" 13 ) 14 15 const ( 16 fontWidth = 11 17 fontHeight = 18 18 blackChar = 1 19 20 // Standard width and height of a captcha image. 21 stdWidth = 240 22 stdHeight = 80 23 24 // Maximum absolute skew factor of a single digit. 25 maxSkew = 0.7 26 27 // Number of background circles. 28 circleCount = 20 29 ) 30 31 var font = [][]byte{ 32 { // 0 33 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 34 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 35 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 36 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 37 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 38 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 39 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 40 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 41 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 42 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 43 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 44 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 45 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 46 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 47 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 48 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 49 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 50 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 51 }, 52 { // 1 53 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 54 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 55 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 56 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 57 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 58 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 59 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 60 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 61 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 62 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 63 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 64 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 65 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 66 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 67 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 68 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 69 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 70 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 71 }, 72 { // 2 73 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 74 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 75 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 76 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 77 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 78 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 79 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 80 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 81 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 82 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 83 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 84 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 85 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 86 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 87 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 88 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 89 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 90 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 91 }, 92 { // 3 93 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 94 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 95 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 96 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 97 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 98 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 99 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 100 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 101 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 102 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 103 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 104 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 105 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 106 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 107 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 108 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 109 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 110 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 111 }, 112 { // 4 113 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 114 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 115 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 116 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 117 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 118 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 119 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 120 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 121 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 122 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 123 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 124 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 125 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 126 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 127 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 128 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 129 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 130 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 131 }, 132 { // 5 133 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 134 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 135 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 136 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 137 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 138 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 139 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 140 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 141 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 142 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 143 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 144 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 145 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 146 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 147 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 148 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 149 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 150 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 151 }, 152 { // 6 153 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 154 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 155 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 156 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 157 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 158 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 160 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 161 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 162 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 163 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 164 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 165 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 166 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 167 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 168 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 169 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 170 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 171 }, 172 { // 7 173 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 174 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 175 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 176 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 177 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 178 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 179 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 180 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 181 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 182 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 183 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 184 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 185 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 186 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 187 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 188 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 189 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 190 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 191 }, 192 { // 8 193 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 194 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 195 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 196 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 197 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 198 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 199 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 200 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 201 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 202 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 203 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 204 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 205 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 206 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 207 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 208 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 209 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 210 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 211 }, 212 { // 9 213 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 214 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 215 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 216 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 217 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 218 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 219 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 220 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 221 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 222 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 223 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 224 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 225 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 226 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 227 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 228 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 229 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 230 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 231 }, 232 } 233 234 type Image struct { 235 *image.Paletted 236 numWidth int //a digit Width 237 numHeight int //a digit Height 238 dotSize int 239 } 240 241 func init() { 242 rand.Seed(int64(time.Second)) 243 } 244 245 // randIntn returns a pseudorandom non-negative int in range [0, n). 246 func randIntn(n int) int { 247 return rand.Intn(n) 248 } 249 250 // rnd returns a random number in range [from, to]. 251 func randInt(from, to int) int { 252 return rand.Intn(to+1-from) + from 253 } 254 255 // randFloat returns a pseudorandom float64 in range [from, to]. 256 func randFloat(from, to float64) float64 { 257 return (to-from)*rand.Float64() + from 258 } 259 260 func randomPalette() color.Palette { 261 p := make([]color.Color, circleCount+1) 262 // Transparent color. 263 p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00} 264 // Primary color. 265 prim := color.RGBA{ 266 uint8(randIntn(129)), 267 uint8(randIntn(129)), 268 uint8(randIntn(129)), 269 0xFF, 270 } 271 p[1] = prim 272 // Circle colors. 273 for i := 2; i <= circleCount; i++ { 274 p[i] = randomBrightness(prim, 255) 275 } 276 return p 277 } 278 279 // NewImage returns a new captcha image of the given width and height with the 280 // given digits, where each digit must be in range 0-9. 281 func NewImage(digits []byte, width, height int) *Image { 282 m := new(Image) 283 m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette()) 284 m.calculateSizes(width, height, len(digits)) 285 // Randomly position captcha inside the image. 286 maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize 287 maxy := height - m.numHeight - m.dotSize*2 288 var border int 289 if width > height { 290 border = height / 5 291 } else { 292 border = width / 5 293 } 294 x := randInt(border, maxx-border) 295 y := randInt(border, maxy-border) 296 // Draw digits. 297 for _, n := range digits { 298 m.drawDigit(font[n], x, y) 299 x += m.numWidth + m.dotSize 300 } 301 // Draw strike-through line. 302 m.strikeThrough() 303 // Apply wave distortion. 304 m.distort(randFloat(5, 10), randFloat(100, 200)) 305 // Fill image with random circles. 306 m.fillWithCircles(circleCount, m.dotSize) 307 return m 308 } 309 310 // encodedPNG encodes an image to PNG and returns 311 // the result as a byte slice. 312 func (m *Image) encodedPNG() []byte { 313 var buf bytes.Buffer 314 if err := png.Encode(&buf, m.Paletted); err != nil { 315 panic(err.Error()) 316 } 317 return buf.Bytes() 318 } 319 320 // WriteTo writes captcha image in PNG format into the given writer. 321 func (m *Image) WriteTo(w io.Writer) (int64, error) { 322 n, err := w.Write(m.encodedPNG()) 323 return int64(n), err 324 } 325 326 func (m *Image) calculateSizes(width, height, ncount int) { 327 // Goal: fit all digits inside the image. 328 var border int 329 if width > height { 330 border = height / 4 331 } else { 332 border = width / 4 333 } 334 // Convert everything to floats for calculations. 335 w := float64(width - border*2) 336 h := float64(height - border*2) 337 // fw takes into account 1-dot spacing between digits. 338 fw := float64(fontWidth + 1) 339 fh := float64(fontHeight) 340 nc := float64(ncount) 341 // Calculate the width of a single digit taking into account only the 342 // width of the image. 343 nw := w / nc 344 // Calculate the height of a digit from this width. 345 nh := nw * fh / fw 346 // Digit too high? 347 if nh > h { 348 // Fit digits based on height. 349 nh = h 350 nw = fw / fh * nh 351 } 352 // Calculate dot size. 353 m.dotSize = int(nh / fh) 354 // Save everything, making the actual width smaller by 1 dot to account 355 // for spacing between digits. 356 m.numWidth = int(nw) - m.dotSize 357 m.numHeight = int(nh) 358 } 359 360 func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) { 361 for x := fromX; x <= toX; x++ { 362 m.SetColorIndex(x, y, colorIdx) 363 } 364 } 365 366 func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) { 367 f := 1 - radius 368 dfx := 1 369 dfy := -2 * radius 370 xo := 0 371 yo := radius 372 373 m.SetColorIndex(x, y+radius, colorIdx) 374 m.SetColorIndex(x, y-radius, colorIdx) 375 m.drawHorizLine(x-radius, x+radius, y, colorIdx) 376 377 for xo < yo { 378 if f >= 0 { 379 yo-- 380 dfy += 2 381 f += dfy 382 } 383 xo++ 384 dfx += 2 385 f += dfx 386 m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx) 387 m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx) 388 m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx) 389 m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx) 390 } 391 } 392 393 func (m *Image) fillWithCircles(n, maxradius int) { 394 maxx := m.Bounds().Max.X 395 maxy := m.Bounds().Max.Y 396 for i := 0; i < n; i++ { 397 colorIdx := uint8(randInt(1, circleCount-1)) 398 r := randInt(1, maxradius) 399 m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx) 400 } 401 } 402 403 func (m *Image) strikeThrough() { 404 maxx := m.Bounds().Max.X 405 maxy := m.Bounds().Max.Y 406 y := randInt(maxy/3, maxy-maxy/3) 407 amplitude := randFloat(5, 20) 408 period := randFloat(80, 180) 409 dx := 2.0 * math.Pi / period 410 for x := 0; x < maxx; x++ { 411 xo := amplitude * math.Cos(float64(y)*dx) 412 yo := amplitude * math.Sin(float64(x)*dx) 413 for yn := 0; yn < m.dotSize; yn++ { 414 r := randInt(0, m.dotSize) 415 m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1) 416 } 417 } 418 } 419 420 func (m *Image) drawDigit(digit []byte, x, y int) { 421 skf := randFloat(-maxSkew, maxSkew) 422 xs := float64(x) 423 r := m.dotSize / 2 424 y += randInt(-r, r) 425 for yo := 0; yo < fontHeight; yo++ { 426 for xo := 0; xo < fontWidth; xo++ { 427 if digit[yo*fontWidth+xo] != blackChar { 428 continue 429 } 430 m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1) 431 } 432 xs += skf 433 x = int(xs) 434 } 435 } 436 437 func (m *Image) distort(amplude float64, period float64) { 438 w := m.Bounds().Max.X 439 h := m.Bounds().Max.Y 440 441 oldm := m.Paletted 442 newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette) 443 444 dx := 2.0 * math.Pi / period 445 for x := 0; x < w; x++ { 446 for y := 0; y < h; y++ { 447 xo := amplude * math.Sin(float64(y)*dx) 448 yo := amplude * math.Cos(float64(x)*dx) 449 newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo))) 450 } 451 } 452 m.Paletted = newm 453 } 454 455 func randomBrightness(c color.RGBA, max uint8) color.RGBA { 456 minc := min3(c.R, c.G, c.B) 457 maxc := max3(c.R, c.G, c.B) 458 if maxc > max { 459 return c 460 } 461 n := randIntn(int(max-maxc)) - int(minc) 462 return color.RGBA{ 463 uint8(int(c.R) + n), 464 uint8(int(c.G) + n), 465 uint8(int(c.B) + n), 466 uint8(c.A), 467 } 468 } 469 470 func min3(x, y, z uint8) (m uint8) { 471 m = x 472 if y < m { 473 m = y 474 } 475 if z < m { 476 m = z 477 } 478 return 479 } 480 481 func max3(x, y, z uint8) (m uint8) { 482 m = x 483 if y > m { 484 m = y 485 } 486 if z > m { 487 m = z 488 } 489 return 490 } 491 492 const ( 493 // Standard length of uniuri string to achive ~95 bits of entropy. 494 StdLen = 16 495 // Length of uniurl string to achive ~119 bits of entropy, closest 496 // to what can be losslessly converted to UUIDv4 (122 bits). 497 UUIDLen = 20 498 ) 499 500 // Standard characters allowed in uniuri string. 501 var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") 502 503 // New returns a new random string of the standard length, consisting of 504 // standard characters. 505 func New() string { 506 return NewLenChars(StdLen, StdChars) 507 } 508 509 // NewLen returns a new random string of the provided length, consisting of 510 // standard characters. 511 func NewLen(length int) string { 512 return NewLenChars(length, StdChars) 513 } 514 515 // NewLenChars returns a new random string of the provided length, consisting 516 // of the provided byte slice of allowed characters (maximum 256). 517 func NewLenChars(length int, chars []byte) string { 518 b := make([]byte, length) 519 r := make([]byte, length+(length/4)) // storage for random bytes. 520 clen := byte(len(chars)) 521 maxrb := byte(256 - (256 % len(chars))) 522 i := 0 523 for { 524 if _, err := io.ReadFull(crand.Reader, r); err != nil { 525 panic("error reading from random source: " + err.Error()) 526 } 527 for _, c := range r { 528 if c >= maxrb { 529 // Skip this number to avoid modulo bias. 530 continue 531 } 532 b[i] = chars[c%clen] 533 i++ 534 if i == length { 535 return string(b) 536 } 537 } 538 } 539 panic("unreachable") 540 } 541 542 // func pic(w http.ResponseWriter, req *http.Request) { 543 // d := make([]byte, 4) 544 // s := NewLen(4) 545 // ss := "" 546 // d = []byte(s) 547 // for v := range d { 548 // d[v] %= 10 549 // ss += strconv.FormatInt(int64(d[v]), 32) 550 // } 551 // w.Header().Set("Content-Type", "image/png") 552 // NewImage(d, 100, 40).WriteTo(w) 553 // fmt.Println(ss) 554 // } 555 556 // func index(w http.ResponseWriter, req *http.Request) { 557 // str := "<meta charset=\"utf-8\"><h3>golang 图片验证码例子</h3><img border=\"1\" src=\"/pic\" alt=\"图片验证码\" onclick=\"this.src='/pic'\" />" 558 // w.Header().Set("Content-Type", "text/html") 559 // w.Write([]byte(str)) 560 // } 561 562 // func main() { 563 // http.HandleFunc("/pic", pic) 564 // http.HandleFunc("/", index) 565 // s := &http.Server{ 566 // Addr: ":8080", 567 // ReadTimeout: 30*time.Second, 568 // WriteTimeout: 30*time.Second, 569 // MaxHeaderBytes: 1 << 20, 570 // } 571 // s.ListenAndServe() 572 // }