设为首页 收藏本站
查看: 1745|回复: 0

[经验分享] nginx-lua实现简单权限控制

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2014-4-2 09:26:48 | 显示全部楼层 |阅读模式
本帖最后由 eeennn 于 2014-4-2 09:28 编辑

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
结合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)


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-16663-1-1.html 上篇帖子: Flume 收集Nginx日志到Hdfs Tail-to-hdfs sink 下篇帖子: 用mingw-w64 编译 x64 位的ffmpeg
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表