您的位置:首页 > 运维架构 > Nginx

nginx-lua实现简单权限控制

2014-04-01 20:32 597 查看
1,依赖软件:nginx(openresty) mysql(存储用户表)redis(存储用户登录token,有效期1周)

create table account(
uid integer not null auto_increment,
username varchar(64),
password varchar(64),
email varchar(256),
primary key(uid),
unique key email(email)
);


nginx配置文件如下:

location = /account {
lua_need_request_body on;
content_by_lua_file /usr/local/FRIENDS/code_lua/account.lua;
}

location = /api {
access_by_lua '
local tokentool = require "tokentool"
local args = ngx.req.get_uri_args(10)
if args.token == nil then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
local ret = tokentool.has_token(args.token)
if ret == ngx.null then
ngx.exit(ngx.HTTP_FORBIDDEN)
elseif ret == false then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
';

content_by_lua '
ngx.say("token ok")
';
}


2,依赖库:nginx-lua , lua-redis(读取redis), lua-mysql(读取mysql), lua-string(加解密)

3,代码如下:

account.lua(入口)

local mysql = require "resty.mysql"
local tokentool = require "tokentool"

-- post only
local method = ngx.req.get_method()
if method ~= "POST" then
ngx.exit(ngx.HTTP_FORBIDDEN)
return
end

-- get args
local args = ngx.req.get_uri_args(10)
if args.act ~= "register" and args.act ~= "login" and args.act ~= "logout" and args.act ~= "updatepwd" then
ngx.exit(ngx.HTTP_BAD_REQUEST)
return
end

local postargs = ngx.req.get_post_args(10)

-- connect to mysql;
local function connect()
local db, err = mysql:new()
if not db then
return false
end
db:set_timeout(1000)

local ok, err, errno, sqlstate = db:connect{
host = "127.0.0.1",
port = 3306,
database = "friends",
user = "root",
password = "",
max_packet_size = 1024 * 1024 }

if not ok then
return false
end
return db
end

function register(pargs)
if pargs.username == nil then
pargs.username = ""
end
if pargs.email == nil or pargs.password == nil then
ngx.exit(ngx.HTTP_BAD_REQUEST)
return
end

local db = connect()
if db == false then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end

local res, err, errno, sqlstate = db:query("insert into account(username, password, email) "
.. "values (\'".. pargs.username .."\',\'".. pargs.password .."\',\'".. pargs.email .."\')")
if not res then
ngx.exit(ngx.HTTP_NOT_ALLOWED)
return
end

local uid = res.insert_id
local token, rawtoken = tokentool.gen_token(uid)

local ret = tokentool.add_token(token, rawtoken)
if ret == true then
ngx.say(token)
else
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
end

function login(pargs)
if pargs.email == nil or pargs.password == nil then
ngx.exit(ngx.HTTP_BAD_REQUEST)
return
end

local db = connect()
if db == false then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end

local res, err, errno, sqlstate = db:query("select uid from account where email=\'".. pargs.email .."\' and password=\'".. pargs.password .."\' limit 1", 1)
if not res then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end
--local cjson = require "cjson"
--ngx.say(cjson.encode(res))
if res[1] == nil then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
local uid = res[1].uid
local token, rawtoken = tokentool.gen_token(uid)

local ret = tokentool.add_token(token, rawtoken)
if ret == true then
ngx.say(token)
else
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
end

function logout(pargs)
if pargs.token == nil then
ngx.exit(ngx.HTTP_BAD_REQUEST)
return
end

tokentool.del_token(pargs.token)
ngx.say("ok")
end

-- to be done
function updatepwd(pargs)
local db = connect()
if db == false then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end
ngx.say(pargs.username .. pargs.newpassword)
end

if args.act == "register" then
register(postargs)
elseif args.act == "login" then
login(postargs)
elseif args.act == "updatepwd" then
updatepwd(postargs)
elseif args.act == "logout" then
logout(postargs)
end


tokentool.lua(用redis存取token,放到序号为1的redis库中)

module("tokentool", package.seeall)
local redis = require "resty.redis"
local aes = require "resty.aes"
local str = require "resty.string"

local alive_time = 3600 * 24 * 7
local redis_host = "127.0.0.1"
local redis_port = 6379

local function connect()
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect(redis_host, redis_port)
if not ok then
return false
end
ok, err = red:select(1)
if not ok then
return false
end
return red
end

function add_token(token, raw_token)
local red = connect()
if red == false then
return false
end

local ok, err = red:setex(token, alive_time, raw_token)
if not ok then
return false
end
return true
end

function del_token(token)
local red = connect()
if red == false then
return
end
red:del(token)
end

function has_token(token)
local red = connect()
if red == false then
return false
end

local res, err = red:get(token)
if not res then
return false
end
return res
end

-- generate token
function gen_token(uid)
local rawtoken = uid .. " " .. ngx.now()
local aes_128_cbc_md5 = aes:new("friends_secret_key")
local encrypted = aes_128_cbc_md5:encrypt(rawtoken)
local token = str.to_hex(encrypted)
return token, rawtoken
end


使用方法:

1,注册用户,返回token
curl -d "username=ciaos&email=aaa@126.com&password=12345" http://localhost/account?act=register 2,登录,返回token
curl -d "email=aaa@126.com&password=12345" http://localhost/account?act=login 3,注销,删除token,返回ok
curl -d "token=0bab442cd24cd055b58665d4156939655d72a7c282c916778ef2c63be9971085" http://localhost/account?act=logout[/code] 
结合codeigniter还需做如下配置

if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php/$1 last;
break;
}

location ~ \.php($|/) {
access_by_lua '
local tokentool = require "tokentool"
local args = ngx.req.get_uri_args(10)
if args.token == nil then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
local ret = tokentool.has_token(args.token)
if ret == ngx.null then
ngx.exit(ngx.HTTP_FORBIDDEN)
elseif ret == false then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
ngx.req.set_uri_args({token=ret})
';
fastcgi_pass   127.0.0.1:9000;
fastcgi_index  index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param   PATH_INFO $fastcgi_path_info;
fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
include        fastcgi_params;
}


需要在网站根目录放置空文件account,不然nginx处理/account时会走到找不到request_filename的这个location里面。

附(ngx-lua的long-polling实现):

--[[ nginx.conf
location /message {
default_type text/plain;
access_by_lua '
local tokentool = require "tokentool"
local args = ngx.req.get_uri_args(10)
if args.token == nil then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
local ret = tokentool.has_token(args.token)
if ret == ngx.null then
ngx.exit(ngx.HTTP_FORBIDDEN)
elseif ret == false then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
ngx.req.set_uri_args({token=ret})
';

content_by_lua_file                     /usr/local/FRIENDS/code_lua/message.lua;
}
--]]

-- message.lua

local redis = require "resty.redis"

local redis_host = "127.0.0.1"
local redis_port = 6379

local function connect()
local red = redis:new()
red:set_timeout(1000 * 1000)
local ok, err = red:connect(redis_host, redis_port)
if not ok then
return false
end

return red
end

local function string_split (string, split)
local list = {}
local pos = 1
if string.find("", split, 1) then -- this would result in endless loops
end
while 1 do
local first, last = string.find(string, split, pos)
if first then -- found
table.insert(list, string.sub(string, pos, first-1))
pos = last+1
else
table.insert(list, string.sub(string, pos))
break
end
end

return list
end

local args = ngx.req.get_uri_args()
local params = string_split(args.token, ' ')
local mymailbox = params[1] .. "_mailbox"

local red = connect()
if red == false then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end

local res, err = red:llen(mymailbox)
if err ~= nil then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end

if res == 0 then
res, err = red:brpop(mymailbox, 1000)
ngx.print('[')
for k, v in ipairs(res) do
if k == 2 then
ngx.print(v)
end
end
ngx.print(']')
else
if res > 10 then
res = 10
end
local i = 0
ngx.print('[')
for i=0, res - 1, 1 do
local msg, _ =  red:rpop(mymailbox)
ngx.print(msg)
if i ~= res - 1 then
ngx.print(',')
end
end
ngx.print(']')
end
ngx.exit(ngx.HTTP_OK)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: