package service import ( "bytes" "encoding/base64" "image" "image/color" "image/png" "math/rand/v2" "strings" "time" "yintai-api/app/common/cache" err2 "yintai-api/app/common/err" "github.com/smbrave/goutil" ) const captchaAlphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" var captchaFont = map[rune][7]byte{ '2': {0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111}, '3': {0b01110, 0b10001, 0b00001, 0b00110, 0b00001, 0b10001, 0b01110}, '4': {0b00100, 0b01000, 0b10000, 0b10010, 0b11111, 0b00010, 0b00010}, '5': {0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110}, '6': {0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110}, '7': {0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000}, '8': {0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110}, '9': {0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100}, 'A': {0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}, 'B': {0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110}, 'C': {0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110}, 'D': {0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110}, 'E': {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111}, 'F': {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000}, 'G': {0b01110, 0b10001, 0b10000, 0b10111, 0b10001, 0b10001, 0b01111}, 'H': {0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}, 'J': {0b00111, 0b00010, 0b00010, 0b00010, 0b00010, 0b10010, 0b01100}, 'K': {0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001}, 'L': {0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}, 'M': {0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001}, 'N': {0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001, 0b10001}, 'P': {0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000}, 'Q': {0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101}, 'R': {0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001}, 'S': {0b01111, 0b10000, 0b10000, 0b01110, 0b00001, 0b00001, 0b11110}, 'T': {0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}, 'U': {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}, 'V': {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100}, 'W': {0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b10101, 0b01010}, 'X': {0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001}, 'Y': {0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100}, 'Z': {0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111}, } func (a *User) Captcha() (string, string) { code := randomCaptcha(4) id := goutil.RandomStr(16) if err := cache.GetRedis().Set(cache.NewKeys().Captcha(id), code, 10*time.Minute).Err(); err != nil { err2.DbErr("", err) } return id, "data:image/png;base64," + base64.StdEncoding.EncodeToString(captchaPNG(code)) } func verifyCaptcha(id, code string) { id = strings.TrimSpace(id) code = strings.TrimSpace(code) if id == "" { err2.CaptchaErr() } key := cache.NewKeys().Captcha(id) redix := cache.GetRedis() saved := redix.Get(key).Val() redix.Del(key) if code == "" || saved == "" || !strings.EqualFold(saved, code) { err2.CaptchaErr() } } func randomCaptcha(n int) string { buf := make([]byte, n) for i := range buf { buf[i] = captchaAlphabet[rand.IntN(len(captchaAlphabet))] } return string(buf) } func captchaPNG(code string) []byte { const scale = 3 charW, charH := 5*scale, 7*scale gap, padX, padY := 10, 14, 12 width := padX*2 + len(code)*charW + (len(code)-1)*gap height := padY*2 + charH + 8 img := image.NewRGBA(image.Rect(0, 0, width, height)) bg := color.RGBA{245, 246, 248, 255} for y := 0; y < height; y++ { for x := 0; x < width; x++ { img.Set(x, y, bg) } } for range 8 { c := color.RGBA{uint8(160 + rand.IntN(70)), uint8(160 + rand.IntN(70)), uint8(160 + rand.IntN(70)), 255} x1, y1 := rand.IntN(width), rand.IntN(height) x2, y2 := rand.IntN(width), rand.IntN(height) drawLine(img, x1, y1, x2, y2, c) } for i, r := range code { glyph, ok := captchaFont[r] if !ok { continue } ox := padX + i*(charW+gap) + rand.IntN(3) - 1 oy := padY + rand.IntN(5) ink := color.RGBA{uint8(rand.IntN(80)), uint8(rand.IntN(80)), uint8(rand.IntN(80)), 255} for row := 0; row < 7; row++ { bits := glyph[row] for col := 0; col < 5; col++ { if bits&(1< x2 { sx = -1 } if y1 > y2 { sy = -1 } err := dx - dy for { if image.Pt(x1, y1).In(img.Bounds()) { img.Set(x1, y1, c) } if x1 == x2 && y1 == y2 { return } e2 := 2 * err if e2 > -dy { err -= dy x1 += sx } if e2 < dx { err += dx y1 += sy } } } func abs(n int) int { if n < 0 { return -n } return n }