在openresy中使用lua-gd库实现验证码功能

一、下载luagd库

下载地址:https://github.com/ittner/lua-gd
注:生成图片主要的类库

二、修改编译引导文件Makefile

将Makefile中某些配置修改为与自身环境相匹配的参数,主要修改以下参数

  1. #输出的动态链接库的名称
  2. OUTFILE=gd.so
  3. #编译参数(固定值,不需修改)
  4. CFLAGS=-O3 -Wall -fPIC $(OMITFP)
  5. #指定编译时包含文件的路径(这里主要指定luajit的安装路径)
  6. CFLAGS+=`$(GDLIBCONFIG) --cflags` -I/usr/local/openresty/luajit/include/luajit-2.1
  7. CFLAGS+=-DVERSION=\"$(VERSION)\"
  8. GDFEATURES=`$(GDLIBCONFIG) --features |sed -e "s/GD_/-DGD_/g"`
  9. LFLAGS=-shared `$(GDLIBCONFIG) --ldflags` `$(GDLIBCONFIG) --libs` -lgd
  10. #指定安装目录(一般是luajit中的lua标准库的目录)
  11. INSTALL_PATH=/usr/local/openresty/luajit/lib/lua/5.1

注:
本人本地的openresty安装目录为/usr/local/openresty,要注意自身所使用的环境中openresty的版本

三、编译生成动态链接库

  1. make

如果编译出错,如果提示gdlib-config not found,则说明libgd库可能没有安装(就是gd库没有安装,lua-gd只是一个lua针对gd的扩展),使用apt-get安装libgd即可:

  1. sudo apt-get install libgd2-dev

注:centos系统:

  1. sudo yum install gd-devel

四、将编译生成的动态链接库gd.so放入到/usr/local/openresty/luajit/lib/lua/5.1下

  1. cp gd.so /usr/local//openresty/luajit/lib/lua/5.1

五、查看lua-gd库的api文档

官方文档:http://ittner.github.io/lua-gd/manual.html

1、验证码生成步骤
  • (1)创建画布

  • (2)创建画面背景并填充

  • (3)向画布上画内容(一般是字符串,这里还包括生成字符串的功能)

  • (4)画干扰线和噪点(这步不是必须的)

  • (5)输出图片(输出到浏览器或输出到文件保存起来)

2、实现代码
  1. local gd = require 'gd'
  2. local _M = {_VERSION = '0.01'}
  3. local mt = {__index = _M }
  4. local math_randomseed = math.randomseed
  5. local math_random = math.random
  6. local substr = string.sub
  7. local strlen = string.len
  8. --[[
  9. -- width 为图片的宽度
  10. -- height 为图片的高度
  11. -- length 为验证码长度
  12. -- type 为验证码类型(1表示纯数字;2表示纯英文字母;3表示数字和英文混合(更多的可以自己扩展))
  13. -- with_noise 指定是否需要噪点
  14. -- captcha 用来存放验证码字符串
  15. -- gd gd库对象
  16. -- img为图片对象
  17. --]]
  18. function _M.new(self, options)
  19. local obj = options or {}
  20. obj['width'] = obj['width'] and tonumber(obj['width']) or 100
  21. obj['height'] = obj['height'] and tonumber(obj['height']) or 60
  22. obj['length'] = obj['length'] and tonumber(obj['length']) or 4
  23. obj['type'] = obj['type'] or 3
  24. obj['with_noise'] = obj['with_noise'] or true
  25. obj['captcha'] = ''
  26. obj['gd'] = gd
  27. obj['img'] = nil
  28. return setmetatable(obj, mt)
  29. end
  30. -- 创建画布
  31. function _M.create(self)
  32. --创建画布
  33. self.img = self.gd.createTrueColor(self.width, self.height)
  34. -- 为画布填充背景色
  35. local bgcolor = self.img:colorAllocate(255, 255, 255)
  36. self.img:filledRectangle(0, 0, self.width, self.height, bgcolor)
  37. end
  38. -- 画噪点
  39. function _M.noise(self)
  40. local point_color
  41. local point_num = self.width/5
  42. for i=1,point_num do
  43. point_color = self.img:colorAllocate(math_random(20, 156), math_random(0, 156), math_random(20, 156))
  44. local x = i*5
  45. self.img:filledEllipse(math_random(x, x+5), math_random(3, self.height-2), 2, 2, point_color)
  46. end
  47. end
  48. -- 生成随机验证码字符串
  49. local function randomCode(type, len)
  50. local str
  51. local code = ''
  52. local code_tab = {}
  53. if type == 1 then
  54. str = '0123456789'
  55. elseif type == 2 then
  56. str = 'abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ'
  57. elseif type == 3 then
  58. str = '123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ'
  59. end
  60. local str_len = strlen(str)
  61. if len >= str_len then
  62. return nil, 'the length is too long'
  63. end
  64. local random_len = str_len - 1
  65. math_randomseed(ngx.time()*math_random(99, 999999999))
  66. for i=1,len do
  67. local idx = math_random(i, random_len)
  68. local tmp = substr(str, idx,idx)
  69. code = code..tmp
  70. code_tab[#code_tab+1] = tmp
  71. end
  72. return code,code_tab
  73. end
  74. -- 画验证码字符串到画布上
  75. function _M.writeCode(self)
  76. local code, tab = randomCode(self.type, self.length)
  77. self.captcha = code
  78. self:check()
  79. local text_color
  80. local x
  81. local y
  82. math_randomseed(ngx.time()*math_random(99, 999999999))
  83. local per = (self.width - 10)/self.length
  84. for i=1,self.length do
  85. text_color = self.img:colorAllocate(math_random(0, 156), math_random(0, 156), math_random(0, 156))
  86. x = math_random((i-1)*per, per*i)
  87. local half_y = self.height/2
  88. y = math_random(half_y, half_y+5)
  89. self.img:string(self.gd.FONT_GIANT, x, y, tab[i], text_color)
  90. end
  91. end
  92. function _M.write(self)
  93. self:writeCode()
  94. if self.with_noise then
  95. self:noise()
  96. end
  97. end
  98. -- 将验证码图片显示到浏览器上
  99. function _M.show(self)
  100. ngx.header['content-type'] = 'image/jpeg'
  101. self:write()
  102. local img_data = self.img:jpegStr(50)
  103. self:close()
  104. ngx.say(img_data)
  105. end
  106. -- 检验是否初始化
  107. function _M.check(self)
  108. if self.img == nil then
  109. self:create()
  110. end
  111. end
  112. -- 保存验证码图片到文件
  113. function _M.save(self, path, quality)
  114. local quality = quality and tonumber(quality) or 80
  115. self:write()
  116. local ok, err = self.img:jpeg(path, quality)
  117. self:close()
  118. return ok,err
  119. end
  120. function _M.close(self)
  121. -- 显式的垃圾回收
  122. self.img = nil
  123. collectgarbage("collect")
  124. end
  125. local im = _M:new()
  126. --local ok, err = im:save('/data/program/orapp/orapi/tmp/captcha/test.jpg')
  127. im:show()

来看看效果图:

加噪点的效果图: